# HTML-Kurs erstellen: Scratch-Stil (TurboWarp eingebettet)

Diese Anleitung beschreibt, wie ein KI-Agent eine eigenständige HTML-Datei im Stil von `scratch.html` erstellt — mit eingebettetem TurboWarp-Editor als zentralem Element, Aufgaben-Sidebar und Fortschritts-System.

> **Hinweis:** Diese Variante eignet sich für visuelle Programmierkurse mit Scratch/TurboWarp. Der TurboWarp-Editor läuft lokal unter `/turbowarp/editor.html`.

---

## Konzept & Layout

```
┌─────────────────────────────────────────┐
│  Header (Titel, zurück-Link)            │
├──────────────┬──────────────────────────┤
│  Sidebar     │  TurboWarp-Editor        │
│  (Aufgaben)  │  (iframe, nimmt rest.    │
│              │   Platz ein)             │
│  - Aufgabe 1 │                          │
│  - Aufgabe 2 │                          │
│  - ...       │                          │
└──────────────┴──────────────────────────┘
│  Footer (Fortschritt)                   │
└─────────────────────────────────────────┘
```

---

## 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>Scratch – Mein Kurs</title>
  <style>
    :root {
      --primary: #f59e0b; --primary-dark: #d97706; --primary-mid: #fbbf24; --primary-light: #fffbeb;
      --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;
    }
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: Inter, 'Segoe UI', system-ui, -apple-system, sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; -webkit-font-smoothing: antialiased; }

    /* Layout */
    .layout { display: flex; height: calc(100vh - 90px); }

    /* Sidebar */
    .sidebar { width: 270px; flex-shrink: 0; background: var(--surface); border-right: 1px solid var(--border); overflow-y: auto; padding: 1rem 0; }
    .sidebar-title { font-size: .75rem; font-weight: 700; text-transform: uppercase; color: var(--muted); padding: 0 1.25rem .75rem; }

    /* Aufgaben-Einträge */
    .task-item { display: flex; align-items: center; gap: .7rem; padding: .65rem 1.25rem; cursor: pointer; border-left: 3px solid transparent; transition: background .12s; }
    .task-item:hover { background: #fef9ee; }
    .task-item.active { background: #fef3c7; border-left-color: var(--primary); }
    .task-num { width: 24px; height: 24px; border-radius: 50%; background: var(--border); color: var(--muted); font-size: .75rem; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
    .task-item.done .task-num { background: var(--green); color: #fff; }

    /* Hauptbereich */
    .content { flex: 1; display: flex; flex-direction: column; min-width: 0; }

    /* Aufgaben-Header */
    .task-header { padding: .75rem 1.25rem; background: var(--surface); border-bottom: 1px solid var(--border); }
    .task-title { font-size: 1rem; font-weight: 700; }
    .task-desc { font-size: .85rem; color: var(--muted); margin-top: .2rem; line-height: 1.5; }
    .task-hint { font-size: .82rem; background: #fef9ee; border: 1px solid #fde68a; border-radius: 6px; padding: .5rem .75rem; margin-top: .5rem; }

    /* TurboWarp-Editor */
    .editor-wrap { flex: 1; position: relative; }
    .editor-wrap iframe { width: 100%; height: 100%; border: none; display: block; }

    /* Aufgaben-Kontrollen */
    .task-controls { padding: .6rem 1.25rem; background: var(--surface); border-top: 1px solid var(--border); display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }

    /* Fortschritts-Footer */
    .prog-panel { position: fixed; bottom: 0; left: 0; right: 0; background: var(--surface); border-top: 1px solid var(--border); padding: .45rem 1rem; display: flex; align-items: center; gap: .65rem; z-index: 100; }
  </style>
</head>
<body>

<header style="background:var(--surface);border-bottom:1px solid var(--border);padding:1.25rem 2rem;border-top:4px solid var(--primary)">
  <a href="index.html" style="color:var(--muted);text-decoration:none;font-size:.85rem">← Zurück</a>
  <h1 style="font-size:1.5rem;font-weight:700;margin-top:.2rem">🐱 Scratch: Mein Kurs</h1>
  <p style="color:var(--muted);font-size:.88rem">Untertitel hier</p>
</header>

<div class="layout">
  <!-- Aufgaben-Sidebar -->
  <aside class="sidebar">
    <div class="sidebar-title">Aufgaben</div>
    <div id="taskList"></div>
  </aside>

  <!-- Hauptbereich -->
  <div class="content">
    <div class="task-header" id="taskHeader">
      <div class="task-title" id="taskTitle">Wähle eine Aufgabe</div>
      <div class="task-desc" id="taskDesc"></div>
      <div class="task-hint" id="taskHint" style="display:none"></div>
    </div>

    <div class="editor-wrap">
      <iframe id="scratchEditor" src="/turbowarp/editor.html" allowfullscreen></iframe>
    </div>

    <div class="task-controls">
      <label style="display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.9rem">
        <input type="checkbox" id="doneCheck" style="width:18px;height:18px;accent-color:var(--green)" onchange="markCurrentDone(this.checked)">
        Aufgabe abgeschlossen
      </label>
      <button class="btn" onclick="prevTask()" id="btnPrev" disabled>← Vorherige</button>
      <button class="btn pri" onclick="nextTask()" id="btnNext">Nächste →</button>
    </div>
  </div>
</div>

<div class="prog-panel">
  <span style="color:var(--muted);font-size:.74rem">Fortschritt:</span>
  <div id="progDots" style="display:flex;gap:.35rem"></div>
  <span id="progLabel" style="color:var(--muted);font-size:.74rem"></span>
</div>

<script>
const COURSE_ID = 'meinscratchkurs';

// ── Aufgaben-Daten ────────────────────────────────────────────────
const TASKS = [
  {
    id: 'figur-bewegen',
    title: '1. Figur bewegen',
    desc: 'Bringe die Figur dazu, sich bei Tastendruck zu bewegen. Nutze die Bausteine "Bewege 10 Schritte" und "Wenn Taste gedrückt".',
    hint: 'Tipp: Schau unter "Bewegung" und "Ereignisse".',
    allowedCategories: ['Bewegung', 'Ereignisse'],
    projectId: '',    // leer = leerer Editor; oder Scratch-Projekt-ID
    starterUrl: '',   // alternativ: URL zu einem .sb3-Starter-Projekt
    bit: 1
  },
  {
    id: 'kostüm-wechseln',
    title: '2. Kostüm wechseln',
    desc: 'Lass die Figur das Kostüm wechseln, wenn sie die Maus berührt.',
    hint: 'Nutze "Wechsle Kostüm" aus "Aussehen" und "wird Mauszeiger berührt?" aus "Fühlen".',
    allowedCategories: ['Aussehen', 'Fühlen', 'Steuerung'],
    projectId: '',
    starterUrl: '',
    bit: 2
  }
];

// ── State ─────────────────────────────────────────────────────────
let currentTaskIdx = 0;
const DONE = new Array(TASKS.length).fill(false);
let IDENTITY = null;
let PB = new Uint8Array(Math.ceil(TASKS.length / 8));

// ── Hilfsfunktionen ───────────────────────────────────────────────
function esc(s) {
  return String(s ?? '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}

// ── Editor laden ──────────────────────────────────────────────────
function loadTask(idx) {
  if (idx < 0 || idx >= TASKS.length) return;
  currentTaskIdx = idx;
  const t = TASKS[idx];

  // Sidebar aktualisieren
  document.querySelectorAll('.task-item').forEach((el, i) => el.classList.toggle('active', i === idx));

  // Header
  document.getElementById('taskTitle').textContent = t.title;
  document.getElementById('taskDesc').textContent = t.desc;
  const hintEl = document.getElementById('taskHint');
  if (t.hint) { hintEl.style.display = ''; hintEl.textContent = t.hint; }
  else hintEl.style.display = 'none';

  // TurboWarp-Editor neu laden
  const iframe = document.getElementById('scratchEditor');
  let src = '/turbowarp/editor.html';
  if (t.projectId) src += '#' + encodeURIComponent(t.projectId);
  if (t.starterUrl) src += (src.includes('?') ? '&' : '?') + 'project_url=' + encodeURIComponent(t.starterUrl);
  // Erlaubte Kategorien als URL-Parameter (falls TurboWarp das unterstützt)
  iframe.src = src;

  // Done-Checkbox
  document.getElementById('doneCheck').checked = DONE[idx];

  // Navigations-Buttons
  document.getElementById('btnPrev').disabled = idx === 0;
  document.getElementById('btnNext').textContent = idx === TASKS.length - 1 ? '✓ Fertig' : 'Nächste →';

  updateProgress();
}

function prevTask() { loadTask(currentTaskIdx - 1); }
function nextTask() {
  if (currentTaskIdx < TASKS.length - 1) loadTask(currentTaskIdx + 1);
}

function markCurrentDone(checked) {
  DONE[currentTaskIdx] = checked;
  // Sidebar-Dot aktualisieren
  const items = document.querySelectorAll('.task-item');
  items[currentTaskIdx]?.classList.toggle('done', checked);
  updateProgress();
  reportHash();
}

// ── Sidebar rendern ───────────────────────────────────────────────
function renderSidebar() {
  const list = document.getElementById('taskList');
  list.innerHTML = TASKS.map((t, i) => `
    <div class="task-item${i === 0 ? ' active' : ''}${DONE[i] ? ' done' : ''}" onclick="loadTask(${i})">
      <div class="task-num">${DONE[i] ? '✓' : i + 1}</div>
      <div style="font-size:.85rem;font-weight:600">${esc(t.title)}</div>
    </div>
  `).join('');
}

// ── Fortschritt ───────────────────────────────────────────────────
function updateProgress() {
  const done = DONE.filter(Boolean).length;
  document.getElementById('progLabel').textContent = `${done} von ${TASKS.length} Aufgaben abgeschlossen`;
  const dots = document.getElementById('progDots');
  dots.innerHTML = DONE.map((d, i) =>
    `<div style="width:10px;height:10px;border-radius:50%;background:${i===currentTaskIdx?'var(--primary)':d?'var(--green)':'var(--border)'};cursor:pointer" onclick="loadTask(${i})"></div>`
  ).join('');
}

// ── Auth & Fortschritts-Sync (wie andere Kurse) ───────────────────
async function hashPhrase(p) {
  const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(p));
  return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2,'0')).join('');
}

function pbGet(bit) { return !!(PB[0] & (1 << bit)); }
function pbSet(bit, v) { if (v) PB[0] |= (1 << bit); else PB[0] &= ~(1 << bit); }

function progEncode() {
  return btoa(String.fromCharCode(...PB)).replace(/\+/g,'-').replace(/\//g,'_').replace(/=/g,'');
}

async function reportHash() {
  if (!IDENTITY) return;
  const hash = progEncode();
  await fetch('api.php', { method:'POST', headers:{'Content-Type':'application/json'},
    body: JSON.stringify({ action:'hash/report', identity:IDENTITY, course:COURSE_ID, hash }) });
}

// ── Boot ─────────────────────────────────────────────────────────
(async () => {
  renderSidebar();
  loadTask(0);

  const phrase = localStorage.getItem('lms_passphrase');
  if (phrase) {
    const identity = await hashPhrase(phrase);
    const res = await fetch('api.php', { method:'POST', headers:{'Content-Type':'application/json'},
      body: JSON.stringify({ action:'hash/get', identity, course:COURSE_ID }) });
    const data = await res.json();
    if (data.ok) {
      IDENTITY = identity;
      // Fortschritt laden
      TASKS.forEach((t, i) => {
        const bit = t.bit;
        DONE[i] = pbGet(bit);
      });
      renderSidebar();
      updateProgress();
    }
  }
})();
</script>
</body>
</html>
```

---

## TurboWarp: Projekt-IDs

- **Leeres Projekt**: `projectId: ''` → leerer Editor
- **Scratch-Projekt**: `projectId: '482162841'` → öffentliche Scratch-Projekt-ID (von scratch.mit.edu)
- **Starter-Datei**: `starterUrl: 'https://example.com/starter.sb3'` → lädt eine .sb3-Datei als Ausgangspunkt

TurboWarp liegt lokal unter `/turbowarp/editor.html` auf dem Server.

---

## Checkliste für den KI-Agenten

- [ ] `COURSE_ID` eindeutig wählen
- [ ] In `index.html` als Kurs-Button verlinken
- [ ] Alle `TASKS` mit eindeutiger `id` und aufsteigendem `bit` (1, 2, 3…)
- [ ] `--primary`-Farbe für den Kurs-Stil setzen (Scratch: `#f59e0b`)
- [ ] `esc()`-Funktion vorhanden und für alle Texte verwendet
- [ ] Fortschritt per `hash/report` gemeldet
- [ ] Auth aus `localStorage['lms_passphrase']` gelesen
- [ ] Alle Texte auf Deutsch
