From c0635da6cc892d03bba871522552bca06ab2394c Mon Sep 17 00:00:00 2001 From: Alex Mueller Date: Tue, 15 Sep 2026 15:19:52 +0200 Subject: [PATCH] feat(ux): implement modern design tokens, resizable workspace splitters, visual test diffs, and engineering competency model --- .gamification.json | 2 +- assets/gamification.js | 55 +-- assets/gamification_data.js | 98 ++-- assets/monaco_ide.js | 95 ++++ assets/style.css | 451 +++++++++++++++++- .../index.html | 251 +++++----- gamification.py | 86 ++-- workspace.html | 260 +++++++++- 8 files changed, 1039 insertions(+), 259 deletions(-) diff --git a/.gamification.json b/.gamification.json index 688c337..65ce884 100644 --- a/.gamification.json +++ b/.gamification.json @@ -10,5 +10,5 @@ "streak_tage": 1, "letzter_aktiver_tag": "2026-09-12", "historie": [], - "zuletzt_aktualisiert": "2026-09-12T13:29:30.562924" + "zuletzt_aktualisiert": "2026-09-15T15:18:26.060048" } \ No newline at end of file diff --git a/assets/gamification.js b/assets/gamification.js index 7204188..55643bc 100644 --- a/assets/gamification.js +++ b/assets/gamification.js @@ -6,33 +6,33 @@ (function () { const DEFAULT_LEVELS = [ - { level: 1, min_xp: 0, max_xp: 150, titel: "Code-Küken 🐣", rang: "Bronze I" }, - { level: 2, min_xp: 151, max_xp: 400, titel: "Code-Detektiv 🔍", rang: "Bronze II" }, - { level: 3, min_xp: 401, max_xp: 800, titel: "Logik-Bastler ⚙️", rang: "Silber I" }, - { level: 4, min_xp: 801, max_xp: 1300, titel: "Klassen-Baumeister 🏗️", rang: "Silber II" }, - { level: 5, min_xp: 1301, max_xp: 2000, titel: "Git-Stratege 🌿", rang: "Gold I" }, - { level: 6, min_xp: 2001, max_xp: 2900, titel: "TDD-Qualitätsprüfer 🧪", rang: "Gold II" }, - { level: 7, min_xp: 2901, max_xp: 4000, titel: "Vererbungs-Meister 🧬", rang: "Platin I" }, - { level: 8, min_xp: 4001, max_xp: 5300, titel: "Exception-Wächter 🛡️", rang: "Platin II" }, - { level: 9, min_xp: 5301, max_xp: 6800, titel: "GUI-Entwickler 🖥️", rang: "Diamant" }, - { level: 10, min_xp: 6801, max_xp: 99999, titel: "Software-Architekt 🏆", rang: "Großmeister" } + { level: 1, min_xp: 0, max_xp: 150, titel: "Junior IT-Trainee I: Syntax & Basistypen", rang: "Stufe 1" }, + { level: 2, min_xp: 151, max_xp: 400, titel: "Junior IT-Trainee II: Kontrollfluss & Logik", rang: "Stufe 2" }, + { level: 3, min_xp: 401, max_xp: 800, titel: "IT-Specialist: Funktionen & Datenstrukturen", rang: "Stufe 3" }, + { level: 4, min_xp: 801, max_xp: 1300, titel: "Software Developer: OOP-Modellierung", rang: "Stufe 4" }, + { level: 5, min_xp: 1301, max_xp: 2000, titel: "DevOps Engineer: Versionskontrolle & Tooling", rang: "Stufe 5" }, + { level: 6, min_xp: 2001, max_xp: 2900, titel: "Quality Engineer: Test-Driven Development (TDD)", rang: "Stufe 6" }, + { level: 7, min_xp: 2901, max_xp: 4000, titel: "Advanced Engineer: Entwurfsmuster & Polymorphie", rang: "Stufe 7" }, + { level: 8, min_xp: 4001, max_xp: 5300, titel: "Systems Engineer: Security & Error Handling", rang: "Stufe 8" }, + { level: 9, min_xp: 5301, max_xp: 6800, titel: "Application Engineer: Schnittstellen & UI", rang: "Stufe 9" }, + { level: 10, min_xp: 6801, max_xp: 99999, titel: "Certified IT Systems Professional (IHK AP2)", rang: "Master-Stufe" } ]; const DEFAULT_BADGES = { - rechenkonegin: { icon: "🧮", name: "Rechenkönig", desc: "Grundlagen G01 & G02 abgeschlossen (Zahlen, Operatoren & Typen gemeistert)." }, - dialog_profi: { icon: "💬", name: "Dialog-Profi", desc: "Grundlagen G03 abgeschlossen (Interaktive Ein-/Ausgabe mit f-Strings)." }, - weichenstellerin: { icon: "🚦", name: "Weichensteller", desc: "Grundlagen G04 abgeschlossen (Bedingungslogik & Schaltjahre gemeistert)." }, - schleifen_dompteurin: { icon: "🎡", name: "Schleifen-Dompteur", desc: "Grundlagen G05 abgeschlossen (While- & For-Schleifen gebändigt)." }, - funktions_zauberin: { icon: "🪄", name: "Funktions-Zauberer", desc: "Grundlagen G06 abgeschlossen (Eigene Funktionen & Module gebaut)." }, - listen_archivarin: { icon: "📋", name: "Listen-Archivar", desc: "Grundlagen G07 & G08 abgeschlossen (Sequenzen & Strings analysiert)." }, - daten_strategin: { icon: "🗃️", name: "Daten-Stratege", desc: "Grundlagen G09 & G10 abgeschlossen (Dicts, Sets & Comprehensions gemeistert)." }, - bug_jaegerin: { icon: "🔍", name: "Bug-Jäger", desc: "Kapitel 00 gelöst (Alle Fehler-Bugs im Warm-up aufgespürt)." }, - erste_architektin: { icon: "🏗️", name: "Erster Architekt", desc: "Kapitel 01 bis 03 gelöst (Erste OOP-Klassen, Konstruktoren & Methoden)." }, - zeit_reisende: { icon: "🌿", name: "Git-Zeitreisender", desc: "Kapitel 04c Git-Versionskontrolle verstanden & Spielstände gesichert." }, - tamagotchi_mama: { icon: "🥚", name: "Tamagotchi-Hüter", desc: "Kapitel 06 Mini-Projekt abgeschlossen (Ein lebendiges Haustier gebaut)." }, - qualitaets_garantin: { icon: "🧪", name: "TDD-Qualitätsgarant", desc: "Kapitel 09 abgeschlossen (Eigene professionelle Unit Tests geschrieben)." }, - oop_grossmeisterin: { icon: "🧬", name: "Vererbungs-Koryphäe", desc: "Kapitel 10 bis 12 abgeschlossen (Vererbung, Polymorphie & Exceptions)." }, - software_architektin: { icon: "🏆", name: "Meister-Entwickler", desc: "Kapitel 16 Master-Projekt abgeschlossen & vollwertige Desktop-App gebaut!" } + rechenkonegin: { icon: "🧮", name: "Zertifikat: Numerische Typen & Arithmetik", desc: "Grundlagen G01 & G02 abgeschlossen (Zahlen, Operatoren & Typen gemeistert)." }, + dialog_profi: { icon: "💬", name: "Zertifikat: Interaktive I/O & String-Interpolation", desc: "Grundlagen G03 abgeschlossen (Interaktive Ein-/Ausgabe mit f-Strings)." }, + weichenstellerin: { icon: "🚦", name: "Zertifikat: Verzweigungslogik & Boolesche Algebra", desc: "Grundlagen G04 abgeschlossen (Bedingungslogik & Kontrollfluss gemeistert)." }, + schleifen_dompteurin: { icon: "🎡", name: "Zertifikat: Iterationslogik & Sequenzverarbeitung", desc: "Grundlagen G05 abgeschlossen (While- & For-Schleifen gemeistert)." }, + funktions_zauberin: { icon: "📐", name: "Zertifikat: Modulare Funktionsarchitektur", desc: "Grundlagen G06 abgeschlossen (Modulare Funktionen & Parameterübergabe)." }, + listen_archivarin: { icon: "📋", name: "Zertifikat: Datenstrukturen: Listen & Slicing", desc: "Grundlagen G07 & G08 abgeschlossen (Sequenzen, Listen & Slicing analysiert)." }, + daten_strategin: { icon: "🗃️", name: "Zertifikat: Assoziative Arrays: Dicts & Sets", desc: "Grundlagen G09 & G10 abgeschlossen (Dicts, Sets & Hash-Lookup O(1))." }, + bug_jaegerin: { icon: "🔍", name: "Zertifikat: Systematisches Code-Debugging", desc: "Warm-up abgeschlossen (Fehleranalyse & Traceback-Debugging)." }, + erste_architektin: { icon: "🏗️", name: "Zertifikat: OOP-Grundlagen: Klassen & Kapselung", desc: "OOP 01 bis 03 gelöst (Klassendefinition, Konstruktoren & Instanzmethoden)." }, + zeit_reisende: { icon: "🌿", name: "Zertifikat: String-Repräsentation & Dunder-Methoden", desc: "OOP 04 gelöst (Spezielle Methoden __str__, __repr__ implementiert)." }, + tamagotchi_mama: { icon: "📦", name: "Zertifikat: State-Machine & Domänenmodellierung", desc: "OOP Mini-Projekt abgeschlossen (Zustandsverwaltung & Objektinteraktion)." }, + qualitaets_garantin: { icon: "🧪", name: "Zertifikat: Automatisierte Testsuites & TDD", desc: "OOP 09 abgeschlossen (Eigene professionelle Unit Tests mit Assertions)." }, + oop_grossmeisterin: { icon: "🧬", name: "Zertifikat: Fortgeschrittene OOP & Robustes Error-Handling", desc: "OOP 10 bis 12 abgeschlossen (Vererbung, Polymorphie & Exceptions)." }, + software_architektin: { icon: "🏆", name: "Zertifikat: Master-Projekt Software Engineering", desc: "Master-Projekt abgeschlossen & vollwertige Anwendungsarchitektur gebaut!" } }; function getGamificationData() { @@ -47,8 +47,8 @@ spieler_name: "Python-Entwickler", xp: 0, level: 1, - titel: "Code-Küken 🐣", - rang: "Bronze I", + titel: "Junior IT-Trainee I: Syntax & Basistypen", + rang: "Stufe 1", geloeste_kapitel: [], bestandene_tests: 0, freigeschaltete_badges: [], @@ -112,7 +112,8 @@ const hud = document.getElementById("gamification-hud"); if (!hud) return; - const avatarIcon = currentLevelObj.titel.split(" ").pop() || "🐣"; + const avatarIcons = ["💻", "🔍", "⚙️", "🏗️", "🌿", "🧪", "🧬", "🛡️", "🖥️", "🏆"]; + const avatarIcon = avatarIcons[data.level - 1] || "💻"; const solvedCount = (data.geloeste_kapitel || []).length; const totalChapters = 27; diff --git a/assets/gamification_data.js b/assets/gamification_data.js index 1a761e2..6dd5fe0 100644 --- a/assets/gamification_data.js +++ b/assets/gamification_data.js @@ -11,13 +11,13 @@ window.GAMIFICATION_DATA = { "streak_tage": 1, "letzter_aktiver_tag": "2026-09-12", "historie": [], - "zuletzt_aktualisiert": "2026-09-12T13:29:30.562924" + "zuletzt_aktualisiert": "2026-09-15T15:18:26.060048" }; window.GAMIFICATION_BADGES_KATALOG = { "rechenkonegin": { "id": "rechenkonegin", "icon": "🧮", - "name": "Rechenkönigin", + "name": "Zertifikat: Numerische Typen & Arithmetik", "desc": "Grundlagen 01 & 02 abgeschlossen (Zahlen, Operatoren & Typen gemeistert).", "xp_bonus": 100, "kriterium": [ @@ -28,7 +28,7 @@ window.GAMIFICATION_BADGES_KATALOG = { "dialog_profi": { "id": "dialog_profi", "icon": "💬", - "name": "Dialog-Profi", + "name": "Zertifikat: Interaktive I/O & String-Interpolation", "desc": "Grundlagen 03 abgeschlossen (Interaktive Ein-/Ausgabe mit f-Strings).", "xp_bonus": 75, "kriterium": [ @@ -38,8 +38,8 @@ window.GAMIFICATION_BADGES_KATALOG = { "weichenstellerin": { "id": "weichenstellerin", "icon": "🚦", - "name": "Weichenstellerin", - "desc": "Grundlagen 04 abgeschlossen (Bedingungslogik & Schaltjahre gemeistert).", + "name": "Zertifikat: Verzweigungslogik & Boolesche Algebra", + "desc": "Grundlagen 04 abgeschlossen (Bedingungslogik & Kontrollfluss gemeistert).", "xp_bonus": 75, "kriterium": [ "lehrpfad_1_grundlagen/04_verzweigungen_und_bedingungen" @@ -48,8 +48,8 @@ window.GAMIFICATION_BADGES_KATALOG = { "schleifen_dompteurin": { "id": "schleifen_dompteurin", "icon": "🎡", - "name": "Schleifen-Dompteurin", - "desc": "Grundlagen 05 abgeschlossen (While- & For-Schleifen gebändigt).", + "name": "Zertifikat: Iterationslogik & Sequenzverarbeitung", + "desc": "Grundlagen 05 abgeschlossen (While- & For-Schleifen gemeistert).", "xp_bonus": 100, "kriterium": [ "lehrpfad_1_grundlagen/05_schleifen_und_wiederholungen" @@ -57,9 +57,9 @@ window.GAMIFICATION_BADGES_KATALOG = { }, "funktions_zauberin": { "id": "funktions_zauberin", - "icon": "🪄", - "name": "Funktions-Zauberin", - "desc": "Grundlagen 06 abgeschlossen (Eigene Funktionen & Module gebaut).", + "icon": "📐", + "name": "Zertifikat: Modulare Funktionsarchitektur", + "desc": "Grundlagen 06 abgeschlossen (Modulare Funktionen & Parameterübergabe).", "xp_bonus": 100, "kriterium": [ "lehrpfad_1_grundlagen/06_funktionen_und_module" @@ -68,8 +68,8 @@ window.GAMIFICATION_BADGES_KATALOG = { "listen_archivarin": { "id": "listen_archivarin", "icon": "📋", - "name": "Listen-Archivarin", - "desc": "Grundlagen 07 & 08 abgeschlossen (Sequenzen & Strings analysiert).", + "name": "Zertifikat: Datenstrukturen: Listen & Slicing", + "desc": "Grundlagen 07 & 08 abgeschlossen (Sequenzen, Listen & Slicing analysiert).", "xp_bonus": 120, "kriterium": [ "lehrpfad_1_grundlagen/07_listen_und_sequenzen", @@ -79,8 +79,8 @@ window.GAMIFICATION_BADGES_KATALOG = { "daten_strategin": { "id": "daten_strategin", "icon": "🗃️", - "name": "Daten-Strategin", - "desc": "Grundlagen 09 & 10 abgeschlossen (Dicts, Sets & Comprehensions gemeistert).", + "name": "Zertifikat: Assoziative Arrays: Dicts & Sets", + "desc": "Grundlagen 09 & 10 abgeschlossen (Dictionaries, Sets & Hash-Lookup O(1)).", "xp_bonus": 150, "kriterium": [ "lehrpfad_1_grundlagen/09_dictionaries_und_sets", @@ -90,8 +90,8 @@ window.GAMIFICATION_BADGES_KATALOG = { "bug_jaegerin": { "id": "bug_jaegerin", "icon": "🔍", - "name": "Bug-Jägerin", - "desc": "Kapitel 00 gelöst (Alle Fehler-Bugs im Warm-up aufgespürt).", + "name": "Zertifikat: Systematisches Code-Debugging", + "desc": "Warm-up abgeschlossen (Fehleranalyse & Traceback-Debugging).", "xp_bonus": 100, "kriterium": [ "lehrpfad_2_oop_einstieg/00_fehlersuche_und_grundlagen" @@ -100,8 +100,8 @@ window.GAMIFICATION_BADGES_KATALOG = { "erste_architektin": { "id": "erste_architektin", "icon": "🏗️", - "name": "Erste Architektin", - "desc": "Kapitel 01 bis 03 gelöst (Erste OOP-Klassen, Konstruktoren & Methoden).", + "name": "Zertifikat: OOP-Grundlagen: Klassen & Kapselung", + "desc": "OOP 01 bis 03 gelöst (Klassendefinition, Konstruktoren & Instanzmethoden).", "xp_bonus": 200, "kriterium": [ "lehrpfad_2_oop_einstieg/01_einstieg_klassen", @@ -111,9 +111,9 @@ window.GAMIFICATION_BADGES_KATALOG = { }, "tamagotchi_mama": { "id": "tamagotchi_mama", - "icon": "🥚", - "name": "Tamagotchi-Mama", - "desc": "Kapitel 06 Mini-Projekt abgeschlossen (Ein lebendiges Haustier gebaut).", + "icon": "📦", + "name": "Zertifikat: State-Machine & Domänenmodellierung", + "desc": "OOP Mini-Projekt abgeschlossen (Zustandsverwaltung & Objektinteraktion).", "xp_bonus": 250, "kriterium": [ "lehrpfad_2_oop_einstieg/06_abschlussprojekt_tamagotchi" @@ -122,8 +122,8 @@ window.GAMIFICATION_BADGES_KATALOG = { "zeit_reisende": { "id": "zeit_reisende", "icon": "🌿", - "name": "Git-Zeitreisende", - "desc": "Kapitel 04c Git-Versionskontrolle verstanden & Spielstände gesichert.", + "name": "Zertifikat: String-Repräsentation & Dunder-Methoden", + "desc": "OOP 04 gelöst (Spezielle Methoden __str__, __repr__ implementiert).", "xp_bonus": 100, "kriterium": [ "lehrpfad_2_oop_einstieg/04_str_und_darstellung" @@ -132,8 +132,8 @@ window.GAMIFICATION_BADGES_KATALOG = { "qualitaets_garantin": { "id": "qualitaets_garantin", "icon": "🧪", - "name": "TDD-Qualitätsgarantin", - "desc": "Kapitel 09 abgeschlossen (Eigene professionelle Unit Tests geschrieben).", + "name": "Zertifikat: Automatisierte Testsuites & TDD", + "desc": "OOP 09 abgeschlossen (Eigene professionelle Unit Tests mit Assertions).", "xp_bonus": 200, "kriterium": [ "lehrpfad_3_fortgeschrittenes_oop/09_eigene_unit_tests_schreiben" @@ -142,8 +142,8 @@ window.GAMIFICATION_BADGES_KATALOG = { "oop_grossmeisterin": { "id": "oop_grossmeisterin", "icon": "🧬", - "name": "Vererbungs-Koryphäe", - "desc": "Kapitel 10 bis 12 abgeschlossen (Vererbung, Polymorphie & Exceptions).", + "name": "Zertifikat: Fortgeschrittene OOP & Robustes Error-Handling", + "desc": "OOP 10 bis 12 abgeschlossen (Vererbung, Polymorphie & Exceptions).", "xp_bonus": 300, "kriterium": [ "lehrpfad_3_fortgeschrittenes_oop/10_vererbung_und_super", @@ -154,8 +154,8 @@ window.GAMIFICATION_BADGES_KATALOG = { "software_architektin": { "id": "software_architektin", "icon": "🏆", - "name": "Meister-Entwicklerin", - "desc": "Kapitel 16 Master-Projekt abgeschlossen & vollwertige Desktop-App gebaut!", + "name": "Zertifikat: Master-Projekt Software Engineering", + "desc": "Master-Projekt abgeschlossen & vollwertige Anwendungsarchitektur gebaut!", "xp_bonus": 500, "kriterium": [ "lehrpfad_3_fortgeschrittenes_oop/16_master_abschlussprojekt" @@ -167,70 +167,70 @@ window.GAMIFICATION_LEVEL_STUFEN = [ "level": 1, "min_xp": 0, "max_xp": 150, - "titel": "Code-Küken 🐣", - "rang": "Bronze I" + "titel": "Junior IT-Trainee I: Syntax & Basistypen", + "rang": "Stufe 1" }, { "level": 2, "min_xp": 151, "max_xp": 400, - "titel": "Code-Detektivin 🔍", - "rang": "Bronze II" + "titel": "Junior IT-Trainee II: Kontrollfluss & Logik", + "rang": "Stufe 2" }, { "level": 3, "min_xp": 401, "max_xp": 800, - "titel": "Logik-Bastlerin ⚙️", - "rang": "Silber I" + "titel": "IT-Specialist: Funktionen & Datenstrukturen", + "rang": "Stufe 3" }, { "level": 4, "min_xp": 801, "max_xp": 1300, - "titel": "Klassen-Baumeisterin 🏗️", - "rang": "Silber II" + "titel": "Software Developer: OOP-Modellierung", + "rang": "Stufe 4" }, { "level": 5, "min_xp": 1301, "max_xp": 2000, - "titel": "Git-Strategin 🌿", - "rang": "Gold I" + "titel": "DevOps Engineer: Versionskontrolle & Tooling", + "rang": "Stufe 5" }, { "level": 6, "min_xp": 2001, "max_xp": 2900, - "titel": "TDD-Qualitätsprüferin 🧪", - "rang": "Gold II" + "titel": "Quality Engineer: Test-Driven Development (TDD)", + "rang": "Stufe 6" }, { "level": 7, "min_xp": 2901, "max_xp": 4000, - "titel": "Vererbungs-Meisterin 🧬", - "rang": "Platin I" + "titel": "Advanced Engineer: Entwurfsmuster & Polymorphie", + "rang": "Stufe 7" }, { "level": 8, "min_xp": 4001, "max_xp": 5300, - "titel": "Exception-Wächterin 🛡️", - "rang": "Platin II" + "titel": "Systems Engineer: Security & Error Handling", + "rang": "Stufe 8" }, { "level": 9, "min_xp": 5301, "max_xp": 6800, - "titel": "GUI-Entwicklerin 🖥️", - "rang": "Diamant" + "titel": "Application Engineer: Schnittstellen & UI", + "rang": "Stufe 9" }, { "level": 10, "min_xp": 6801, "max_xp": 99999, - "titel": "Software-Architektin 🏆", - "rang": "Großmeisterin" + "titel": "Certified IT Systems Professional (IHK AP2)", + "rang": "Master-Stufe" } ]; diff --git a/assets/monaco_ide.js b/assets/monaco_ide.js index 92b0f17..9f3139b 100644 --- a/assets/monaco_ide.js +++ b/assets/monaco_ide.js @@ -184,6 +184,101 @@ suggestErrorInterpreter(result.rawOutput); } } + + // Strukturierten visuellen Test-Report mit Diff-Inspektor rendern + if (result && (result.total > 0 || result.rawOutput)) { + renderVisualTestReport(result); + } + } + + window.jumpToEditorLine = function(line) { + if (monacoEditor && line) { + monacoEditor.revealLineInCenter(line); + monacoEditor.setPosition({ lineNumber: line, column: 1 }); + monacoEditor.focus(); + } + }; + + function renderVisualTestReport(result) { + const term = document.getElementById("workspace-terminal-output"); + if (!term) return; + + const cases = []; + const raw = result.rawOutput || ""; + + const lineRegex = /(test_\w+)\s+\([^)]+\)\s+\.\.\.\s+(ok|FAIL|ERROR)/g; + let match; + while ((match = lineRegex.exec(raw)) !== null) { + cases.push({ + name: match[1], + passed: match[2] === "ok", + status: match[2] + }); + } + + const fileLineMatch = raw.match(/File\s+["'].*?aufgabe\.\w+["'],\s+line\s+(\d+)/i); + const errorLine = fileLineMatch ? parseInt(fileLineMatch[1], 10) : null; + + let expectedVal = null; + let actualVal = null; + const assertEqualMatch = raw.match(/AssertionError:\s*(.*?)\s*!=\s*(.*)/); + if (assertEqualMatch) { + actualVal = assertEqualMatch[1].trim(); + expectedVal = assertEqualMatch[2].trim(); + } + + const reportCard = document.createElement("div"); + reportCard.style.marginTop = "14px"; + + const total = result.total || cases.length || 1; + const passed = result.passed !== undefined ? result.passed : (result.success ? total : 0); + const isSuccess = result.success; + + reportCard.innerHTML = ` +
+ ${isSuccess ? '🎉 Alle ' + total + ' Tests bestanden!' : '⚠️ ' + (total - passed) + ' von ' + total + ' Test(s) fehlgeschlagen'} + ${passed} / ${total} Tests OK +
+ `; + + if (cases.length > 0) { + cases.forEach(c => { + const card = document.createElement("div"); + card.className = "test-case-card"; + const hasErr = !c.passed; + + card.innerHTML = ` +
+ + ${c.passed ? '✓' : '✕'} ${escapeHtml(c.name)} + + ${c.passed ? 'Bestanden' : 'Fehlgeschlagen ▼'} +
+
+ ${hasErr ? ` +
+ Meldung: Test fehlgeschlagen. +
+ ${expectedVal ? ` +
+
Erwartet: ${escapeHtml(expectedVal)}
+
Erhalten: ${escapeHtml(actualVal)}
+
+ ` : ''} + ${errorLine ? ` + + ` : ''} + ` : '
Dieser Testfall wurde ohne Fehler bestanden.
'} +
+ `; + reportCard.appendChild(card); + }); + } + + term.appendChild(reportCard); + term.scrollTop = term.scrollHeight; } // 4. Hilfsfunktionen diff --git a/assets/style.css b/assets/style.css index 136dad3..52f17c8 100644 --- a/assets/style.css +++ b/assets/style.css @@ -9,6 +9,14 @@ --primary-light: #eff6ff; --primary-glow: rgba(29, 78, 216, 0.12); + --brand-50: #eff6ff; + --brand-100: #dbeafe; + --brand-500: #3b82f6; + --brand-600: #2563eb; + --brand-700: #1d4ed8; + + /* Neutral Scale (Slate Palette) */ + --slate-950: #090d16; --slate-900: #0f172a; --slate-800: #1e293b; --slate-700: #334155; @@ -20,6 +28,23 @@ --slate-100: #f1f5f9; --slate-50: #f8fafc; + /* Semantic Status Tokens (WCAG AA & AAA Compliant) */ + --status-success: #059669; + --status-success-bg: rgba(5, 150, 105, 0.08); + --status-success-border: rgba(5, 150, 105, 0.20); + + --status-warning: #b45309; + --status-warning-bg: rgba(217, 119, 6, 0.08); + --status-warning-border: rgba(217, 119, 6, 0.20); + + --status-danger: #dc2626; + --status-danger-bg: rgba(220, 38, 38, 0.08); + --status-danger-border: rgba(220, 38, 38, 0.20); + + --status-info: #2563eb; + --status-info-bg: rgba(37, 99, 235, 0.08); + --status-info-border: rgba(37, 99, 235, 0.20); + --emerald: #059669; --emerald-light: #f0fdf4; @@ -36,31 +61,44 @@ --success-light: #f0fdf4; /* Surfaces & Backgrounds */ + --bg-canvas: #f8fafc; --bg-body: #f8fafc; --bg-surface: #ffffff; + --bg-surface-hover: #f1f5f9; --bg-subtle: #f1f5f9; --bg-sidebar: #0f172a; - --bg-code: #0b0f19; + --bg-code: #090d16; - /* Borders */ + /* Border-First Design System */ + --border-subtle: rgba(15, 23, 42, 0.08); + --border-default: rgba(15, 23, 42, 0.12); + --border-hover: rgba(15, 23, 42, 0.24); --border-color: #e2e8f0; --border-dark: #1e293b; - /* Typography */ - --text-main: #0f172a; - --text-body: #334155; - --text-muted: #64748b; + /* Typography (WCAG AAA for body text) */ + --text-main: #0f172a; /* Kontrast > 18:1 auf Weiß */ + --text-primary: #0f172a; + --text-body: #334155; /* Kontrast > 10:1 auf Weiß */ + --text-secondary: #334155; + --text-muted: #64748b; /* Kontrast > 5.4:1 auf Weiß (Sicherer AA Standard) */ --text-inverse: #f8fafc; - --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; + --font-sans: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; --font-mono: 'JetBrains Mono', 'Fira Code', 'SFMono-Regular', Consolas, Menlo, monospace; + /* Cognitive Load Limit for Text Containers */ + --content-max-width: 70ch; + /* Elevation */ + --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.04); --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.06), 0 2px 4px -2px rgba(0, 0, 0, 0.04); --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -4px rgba(0, 0, 0, 0.03); --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.08), 0 8px 10px -6px rgba(0, 0, 0, 0.03); + --focus-ring: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--primary); + --radius-xs: 4px; --radius-sm: 6px; --radius-md: 10px; --radius-lg: 14px; @@ -3111,4 +3149,403 @@ tbody tr:hover { color: #fca5a5 !important; } +/* ========================================================================== + ENGINEERING-GRADE COMPONENT SYSTEM & DIDACTIC LAYOUT (WCAG AAA) + ========================================================================== */ + +/* --- Reading Width Limitation (Cognitive Load Reduction) --- */ +.content-readable, +.card p, +.box p { + max-width: var(--content-max-width); +} + +/* --- Standard Buttons --- */ +.btn-primary { + background-color: var(--brand-600); + color: #ffffff !important; + border: 1px solid var(--brand-700); + box-shadow: var(--shadow-xs); + font-weight: 600; +} +.btn-primary:hover { + background-color: var(--brand-700); +} + +.btn-secondary { + background-color: var(--bg-surface); + color: var(--text-primary) !important; + border: 1px solid var(--border-default); + box-shadow: var(--shadow-xs); +} +.btn-secondary:hover { + background-color: var(--bg-surface-hover); + border-color: var(--border-hover); +} + +.btn-ghost { + background-color: transparent; + color: var(--text-secondary) !important; + border: 1px solid transparent; +} +.btn-ghost:hover { + background-color: var(--bg-subtle); + color: var(--text-primary) !important; +} + +.btn-danger { + background-color: var(--status-danger-bg); + color: var(--status-danger) !important; + border: 1px solid var(--status-danger-border); +} +.btn-danger:hover { + background-color: var(--status-danger); + color: #ffffff !important; +} + +.btn:focus-visible { + outline: none; + box-shadow: var(--focus-ring); +} + +/* --- Badges & Pills (Engineering Grade) --- */ +.badge { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 3px 8px; + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 600; + border-radius: var(--radius-full); + letter-spacing: 0.02em; +} + +.badge-neutral { + background: var(--bg-subtle); + color: var(--text-secondary); + border: 1px solid var(--border-subtle); +} + +.badge-success { + background: var(--status-success-bg); + color: var(--status-success); + border: 1px solid var(--status-success-border); +} + +.badge-warning { + background: var(--status-warning-bg); + color: var(--status-warning); + border: 1px solid var(--status-warning-border); +} + +.badge-danger { + background: var(--status-danger-bg); + color: var(--status-danger); + border: 1px solid var(--status-danger-border); +} + +.badge-info { + background: var(--status-info-bg); + color: var(--status-info); + border: 1px solid var(--status-info-border); +} + +/* --- Code-Block Container (with Copy Action) --- */ +.code-block { + background: var(--bg-code); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + margin: 16px 0; + overflow: hidden; +} + +.code-block-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 14px; + background: rgba(255, 255, 255, 0.03); + border-bottom: 1px solid var(--border-subtle); +} + +.code-block-title { + font-family: var(--font-mono); + font-size: 0.78rem; + color: #94a3b8; +} + +.code-copy-btn { + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 600; + color: #cbd5e1; + background: rgba(255, 255, 255, 0.08); + border: 1px solid rgba(255, 255, 255, 0.1); + padding: 3px 9px; + border-radius: var(--radius-xs); + cursor: pointer; + transition: all 0.15s ease; +} +.code-copy-btn:hover { + background: rgba(255, 255, 255, 0.16); + color: #ffffff; +} + +/* --- Didaktische Boxen: Gotchas (Stolperfallen) & Syntax-Boxen --- */ +.box-gotcha { + background: #fffbeb; + border: 1px solid #fde68a; + border-left: 4px solid #d97706; + border-radius: var(--radius-sm); + padding: 14px 18px; + margin: 16px 0; + color: #92400e; +} +.box-gotcha h4 { + margin: 0 0 6px 0; + color: #78350f; + font-size: 0.95rem; + display: flex; + align-items: center; + gap: 6px; +} + +.box-syntax { + background: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: var(--radius-sm); + padding: 14px 18px; + margin: 16px 0; +} +.box-syntax h4 { + margin: 0 0 8px 0; + color: var(--text-primary); + font-size: 0.92rem; + display: flex; + align-items: center; + gap: 6px; +} + +/* --- Subgoal Cards (Cowan Limit 3-4 Steps) --- */ +.subgoal-item { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 12px 14px; + background: #ffffff; + border: 1px solid var(--border-default); + border-radius: var(--radius-sm); + margin-bottom: 8px; + transition: all 0.15s ease; +} +.subgoal-item:hover { + border-color: var(--primary); + box-shadow: var(--shadow-xs); +} +.subgoal-badge { + font-family: var(--font-mono); + font-size: 0.78rem; + font-weight: 700; + color: var(--brand-700); + background: var(--brand-50); + border: 1px solid var(--brand-100); + padding: 3px 8px; + border-radius: var(--radius-xs); + flex-shrink: 0; +} + +/* --- Persistent Sticky Launch Action Bar --- */ +.sticky-launch-bar { + position: sticky; + bottom: 0; + left: 0; + right: 0; + background: rgba(15, 23, 42, 0.94); + backdrop-filter: blur(8px); + border-top: 1px solid rgba(255, 255, 255, 0.1); + padding: 12px 24px; + display: flex; + justify-content: space-between; + align-items: center; + z-index: 50; +} +.btn-cta-launch { + background: #2563eb; + color: #ffffff !important; + font-weight: 700; + font-size: 0.95rem; + padding: 10px 20px; + border-radius: var(--radius-sm); + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 8px; + box-shadow: 0 2px 10px rgba(37, 99, 235, 0.35); + transition: all 0.15s ease; +} +.btn-cta-launch:hover { + background: #1d4ed8; + transform: translateY(-1px); +} + +/* --- Resizable Workspace Splitters (Gutter) --- */ +.gutter-col { + width: 5px; + background: rgba(255, 255, 255, 0.08); + cursor: col-resize; + position: relative; + transition: background 0.15s ease; + z-index: 20; + flex-shrink: 0; +} +.gutter-col::after { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: -5px; + right: -5px; + cursor: col-resize; +} +.gutter-col:hover, +.gutter-col.dragging { + background: #38bdf8; +} + +.gutter-row { + height: 5px; + background: rgba(255, 255, 255, 0.08); + cursor: row-resize; + position: relative; + transition: background 0.15s ease; + z-index: 20; + flex-shrink: 0; +} +.gutter-row::after { + content: ""; + position: absolute; + left: 0; + right: 0; + top: -5px; + bottom: -5px; + cursor: row-resize; +} +.gutter-row:hover, +.gutter-row.dragging { + background: #38bdf8; +} + +/* --- Structured Visual Test Runner Cards --- */ +.test-summary-badge { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 14px; + border-radius: var(--radius-sm); + font-weight: 700; + font-size: 0.88rem; + margin-bottom: 10px; +} +.test-summary-badge.passed { + background: rgba(16, 185, 129, 0.15); + color: #34d399; + border: 1px solid rgba(16, 185, 129, 0.3); +} +.test-summary-badge.failed { + background: rgba(239, 68, 68, 0.15); + color: #f87171; + border: 1px solid rgba(239, 68, 68, 0.3); +} + +.test-case-card { + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: var(--radius-sm); + background: #090d16; + margin-bottom: 8px; + overflow: hidden; +} +.test-case-header { + padding: 8px 12px; + background: #0f172a; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + font-size: 0.84rem; + user-select: none; +} +.test-case-header:hover { + background: #1e293b; +} +.test-case-details { + padding: 10px 14px; + font-family: var(--font-mono); + font-size: 0.82rem; + line-height: 1.5; + border-top: 1px solid rgba(255, 255, 255, 0.06); +} +.test-diff-inspector { + background: #060911; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 4px; + padding: 8px 12px; + margin: 6px 0; + font-size: 0.8rem; +} +.test-diff-expected { + color: #4ade80; +} +.test-diff-actual { + color: #f87171; +} +.btn-jump-code { + background: rgba(56, 189, 248, 0.12); + color: #38bdf8; + border: 1px solid rgba(56, 189, 248, 0.25); + padding: 4px 10px; + border-radius: 4px; + font-size: 0.76rem; + font-family: var(--font-sans); + font-weight: 600; + cursor: pointer; + transition: all 0.15s ease; + margin-top: 6px; +} +.btn-jump-code:hover { + background: rgba(56, 189, 248, 0.25); + color: #ffffff; +} + +/* ========================================================================== + DARK THEME OVERRIDES ([data-theme="dark"]) + ========================================================================== */ +[data-theme="dark"] { + --bg-canvas: #090d16; + --bg-body: #090d16; + --bg-surface: #0f172a; + --bg-surface-hover: #1e293b; + --bg-subtle: rgba(255, 255, 255, 0.04); + --bg-code: #05080f; + + --border-subtle: rgba(255, 255, 255, 0.08); + --border-default: rgba(255, 255, 255, 0.12); + --border-hover: rgba(255, 255, 255, 0.25); + + --text-main: #f8fafc; + --text-primary: #f8fafc; + --text-body: #cbd5e1; + --text-secondary: #cbd5e1; + --text-muted: #94a3b8; + + --brand-500: #3b82f6; + --brand-600: #60a5fa; + + --shadow-xs: none; + --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.4); + --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.5); + --focus-ring: 0 0 0 2px #090d16, 0 0 0 4px #3b82f6; +} + + diff --git a/courses/bash/01_navigation_dateien_und_globbing/index.html b/courses/bash/01_navigation_dateien_und_globbing/index.html index f5607cb..f51cdec 100644 --- a/courses/bash/01_navigation_dateien_und_globbing/index.html +++ b/courses/bash/01_navigation_dateien_und_globbing/index.html @@ -6,148 +6,167 @@ Bash 01: Terminal-Navigation & Globbing +
-
🐧 Linux Bash & Cloud DevOps
+
+ 🐧 Linux Bash & Cloud DevOps + Modul 01 von 16 + ⏱️ ca. 15 Min. + 🎓 IHK AP1 & AP2 Relevanz +

Bash 01: Terminal-Navigation, Dateioperationen & Globbing

-

Beherrsche den Linux-Dateibaum, lerne blitzschnelle Pfad-Navigation und automatisiere Dateioperationen mit Wildcards und Brace Expansion.

+

Beherrsche den Linux-Dateibaum, lerne blitzschnelle Pfad-Navigation und automatisiere Dateioperationen mit Wildcards und Brace Expansion.

- -
-

🧭 1. Der Unix-Dateibaum & Pfad-Navigation

-

In Unix gibt es keine Laufwerksbuchstaben (wie C: oder D:). Alles entspringt dem Root-Verzeichnis / (Single Root Tree). Wir unterscheiden strikt zwischen absoluten und relativen Pfaden:

- -
# Wo bin ich?
+        
+ +
+ +
+

🧭 1. Der Unix-Dateibaum & Pfad-Navigation

+

In Unix gibt es keine Laufwerksbuchstaben (wie C:). Alles entspringt dem Root-Verzeichnis / (Single Root Tree). Wir unterscheiden strikt zwischen absoluten Pfaden (beginnen immer mit /) und relativen Pfaden (relativ zum aktuellen Arbeitsverzeichnis pwd).

+ +
+
+ bash • Navigation & Pfade + +
+
# Wo bin ich?
 pwd                        # Ausgabe: /home/devops/projects
 
 # Relative vs. Absolute Navigation:
-cd /var/log/nginx          # Absoluter Pfad: Beginnt immer mit '/'
+cd /var/log/nginx          # Absoluter Pfad
 cd ../../etc               # Relativer Pfad: Zwei Ebenen hoch, dann in 'etc'
-cd ~                       # Wechselt ins Home-Verzeichnis (/home/user)
-cd -                       # Wechselt zum vorherigen Verzeichnis zurück
+cd ~                       # Home-Verzeichnis
+cd -                       # Vorheriges Verzeichnis
+
-# Detailliertes Listing mit Berechtigungen und Dateigrößen: -ls -lah # -l: Long Format, -a: Versteckte Dateien, -h: Human-readable (KB/MB)
-
+
+
🏢 Didaktische Analogie: Das Hochregallager des Betriebssystems
+

Der Dateibaum: Stell dir das Linux-Dateisystem wie ein gigantisches Logistikzentrum vor. / ist das Haupttor. Mit mkdir -p stellst du mit einer einzigen Anweisung eine ganze Regalreihe mit Fächern auf.

+

Globbing (* und {a,b}): Statt jedes Paket einzeln zu scannen, hältst du eine Schablone hoch: *.yaml greift automatisch alle Konfigurationen, während {dev,prod} im Handumdrehen zwei Umgebungen parallel anlegt!

+
+ - -
-

📁 2. Dateisystem-Operationen & Mächtiges Globbing

-

Als DevOps-Ingenieur erstellst, kopierst und reorganisierst du Konfigurationsdateien im Sekundentakt. Die Shell bietet hierfür mächtige Werkzeuge:

+ +
+

🎯 2. Dein Arbeitsauftrag: 4 Subgoals (Teilziele)

+

Bearbeite die Aufgaben in aufgabe.sh schrittweise:

-
# 🎯 1. Verzeichnisse verschachtelt anlegen:
-mkdir -p app/{config,src/{models,views},logs,tests}
+              
+ Subgoal 1 +
+ Projektverzeichnisse anlegen: +

Implementiere erstelle_projektstruktur(basis_dir) mit mkdir -p für src/components, src/utils, docs/api und backup/logs.

+
+
-# 🎯 2. Dateien mit Brace Expansion massenweise erstellen: -touch app/src/models/{user,order,payment}.py app/config/{dev,staging,prod}.yaml +
+ Subgoal 2 +
+ Testdateien erzeugen: +

Erstelle mit touch und Brace Expansion Beispieldateien in den angelegten Ordnern.

+
+
-# 🎯 3. Shell-Globbing (Wildcards): -cp app/config/*.yaml /backup/configs/ # Alle .yaml Dateien kopieren -mv app/logs/app_2026-??.log /archive/logs/ # Matcht genau 2 Zeichen für den Monat (z.B. 01..12) -rm -rf /tmp/build_cache/ # Rekursives Löschen ohne Bestätigung
+
+ Subgoal 3 +
+ Dateien kopieren & Globbing: +

Kopiere mit cp und Wildcards (*.yaml, *.log) alle Konfigurationsdateien in das Backup-Verzeichnis.

+
+
-
-
🏢 Didaktische Analogie: Das Logistikzentrum des Betriebssystems
-

Der Dateibaum: Stell dir das Dateisystem wie ein riesiges, strukturiertes Hochregallager vor. / ist der Haupteingang. mkdir -p baut dir sofort eine ganze Gasse mit Fachböden auf.

-

Globbing (* und {a,b}): Anstatt jedes Paket einzeln mit der Hand zu scannen, hältst du eine Schablone hoch: *.yaml greift automatisch alle blauen Kisten aus dem Regal, während {dev,prod} mit einem Handgriff zwei vorgefertigte Etiketten druckt!

-
-
- - -
-

📑 3. Cheat-Sheet: Navigation & Wildcards

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Befehl / MusterBeispielBedeutung & DevOps-Einsatz
mkdir -p <pfad>mkdir -p /srv/app/releasesErstellt Elternverzeichnisse automatisch ohne Fehler
cp -r <src> <dst>cp -r ./templates /etc/nginx/Kopiert ganze Verzeichnisbäume rekursiv
mv <src> <dst>mv old.env .env.backupVerschiebt oder benennt Dateien atomar um
rm -rf <pfad>rm -rf ./temp_workspaceLöscht rekursiv und erzwingt Löschen ohne Prompt
* (Wildcard)rm -f *.logMatcht 0 oder beliebig viele Zeichen
? (Single Char)ls node_v?.logMatcht exakt ein einziges Zeichen
{a,b,c}touch {app,db}_{1..3}.confBrace Expansion zur raschen Permutationserstellung
-
-
- - -
-

🎯 Aufgaben & Checkliste in aufgabe.sh

-
- - -
-
- - -
-
- - -
-
- - -
-
- - +
+ Subgoal 4 +
+ Temporäre Dateien bereinigen: +

Nutze rm -f und rm -rf, um Cache- und temporäre Logdateien sauber zu entfernen.

+
+
+
-
-
💡 DevOps Pro-Tipp: Quoting in Shell-Skripten
-

Setze Variablen bei Pfadangaben in Bash-Skripten stets in doppelte Anführungszeichen: "$basis_dir/src/components". Das verhindert gefürchtete Word Splitting Fehler, falls ein Verzeichnisname einmal Leerzeichen enthält!

-
+ +
+ +
+

⚠️ Häufige Stolperfallen (Pre-Mortem)

+

1. Fehlendes -p bei mkdir: Wenn du mkdir a/b/c aufrufst und Ordner a noch nicht existiert, bricht die Shell mit No such file or directory ab. Verwende immer mkdir -p!

+

2. Word Splitting ohne Quotes: Setze Variablenpfade in Skripten stets in doppelte Anführungszeichen: "$basis_dir/src", sonst führen Leerzeichen im Ordnernamen zu fatalen Syntaxfehlern.

+
-
- 💻 In Web-IDE öffnen → - Weiter zu Bash 02 → + +
+

📌 Syntax-Spickzettel: Shell-Muster

+ + + + + + + + + + + + + + + + + + + + + + + + + +
MusterBedeutung
*Matcht 0 bis unendlich viele Zeichen
?Matcht exakt 1 Zeichen
{a,b}Brace Expansion (Permutation)
mkdir -pErstellt Elternordner automatisch
+
+ + +
+

💡 IHK-Prüfungstipp (AP1)

+

In der IHK-Abschlussprüfung Teil 1 wird regelmäßig der Unterschied zwischen relativer und absoluter Pfadangabe sowie die Bedeutung von cd .. und ls -l abgefragt.

+
- +
+ + +
+
+ 🎯 Status: Bereit für die Praxis (4 Teilziele definiert) +
+
+ ← Dashboard + + 💻 In Web-IDE öffnen & Teilziel 1 anpacken → + +
+
diff --git a/gamification.py b/gamification.py index a39002a..83e3626 100644 --- a/gamification.py +++ b/gamification.py @@ -21,26 +21,26 @@ ROOT_DIR = Path(__file__).parent.resolve() DATA_JSON_PATH = ROOT_DIR / ".gamification.json" DATA_JS_PATH = ROOT_DIR / "assets" / "gamification_data.js" -# 10 Entwickler-Level mit XP-Schwellen und Titeln +# 10 Entwickler-Level mit XP-Schwellen und professionellen IT-Kompetenzstufen LEVEL_STUFEN = [ - {"level": 1, "min_xp": 0, "max_xp": 150, "titel": "Code-Küken 🐣", "rang": "Bronze I"}, - {"level": 2, "min_xp": 151, "max_xp": 400, "titel": "Code-Detektivin 🔍", "rang": "Bronze II"}, - {"level": 3, "min_xp": 401, "max_xp": 800, "titel": "Logik-Bastlerin ⚙️", "rang": "Silber I"}, - {"level": 4, "min_xp": 801, "max_xp": 1300, "titel": "Klassen-Baumeisterin 🏗️", "rang": "Silber II"}, - {"level": 5, "min_xp": 1301, "max_xp": 2000, "titel": "Git-Strategin 🌿", "rang": "Gold I"}, - {"level": 6, "min_xp": 2001, "max_xp": 2900, "titel": "TDD-Qualitätsprüferin 🧪", "rang": "Gold II"}, - {"level": 7, "min_xp": 2901, "max_xp": 4000, "titel": "Vererbungs-Meisterin 🧬", "rang": "Platin I"}, - {"level": 8, "min_xp": 4001, "max_xp": 5300, "titel": "Exception-Wächterin 🛡️", "rang": "Platin II"}, - {"level": 9, "min_xp": 5301, "max_xp": 6800, "titel": "GUI-Entwicklerin 🖥️", "rang": "Diamant"}, - {"level": 10, "min_xp": 6801, "max_xp": 99999, "titel": "Software-Architektin 🏆", "rang": "Großmeisterin"}, + {"level": 1, "min_xp": 0, "max_xp": 150, "titel": "Junior IT-Trainee I: Syntax & Basistypen", "rang": "Stufe 1"}, + {"level": 2, "min_xp": 151, "max_xp": 400, "titel": "Junior IT-Trainee II: Kontrollfluss & Logik", "rang": "Stufe 2"}, + {"level": 3, "min_xp": 401, "max_xp": 800, "titel": "IT-Specialist: Funktionen & Datenstrukturen", "rang": "Stufe 3"}, + {"level": 4, "min_xp": 801, "max_xp": 1300, "titel": "Software Developer: OOP-Modellierung", "rang": "Stufe 4"}, + {"level": 5, "min_xp": 1301, "max_xp": 2000, "titel": "DevOps Engineer: Versionskontrolle & Tooling", "rang": "Stufe 5"}, + {"level": 6, "min_xp": 2001, "max_xp": 2900, "titel": "Quality Engineer: Test-Driven Development (TDD)", "rang": "Stufe 6"}, + {"level": 7, "min_xp": 2901, "max_xp": 4000, "titel": "Advanced Engineer: Entwurfsmuster & Polymorphie", "rang": "Stufe 7"}, + {"level": 8, "min_xp": 4001, "max_xp": 5300, "titel": "Systems Engineer: Security & Error Handling", "rang": "Stufe 8"}, + {"level": 9, "min_xp": 5301, "max_xp": 6800, "titel": "Application Engineer: Schnittstellen & UI", "rang": "Stufe 9"}, + {"level": 10, "min_xp": 6801, "max_xp": 99999, "titel": "Certified IT Systems Professional (IHK AP2)", "rang": "Master-Stufe"}, ] -# 14 freischaltbare Trophäen mit didaktischen Kriterien (abgestimmt auf neue Lehrpfad-Pfade) +# 14 freischaltbare IT-Kompetenz-Zertifikate (abgestimmt auf IHK-Rahmenlehrplan) BADGES_KATALOG = { "rechenkonegin": { "id": "rechenkonegin", "icon": "🧮", - "name": "Rechenkönigin", + "name": "Zertifikat: Numerische Typen & Arithmetik", "desc": "Grundlagen 01 & 02 abgeschlossen (Zahlen, Operatoren & Typen gemeistert).", "xp_bonus": 100, "kriterium": [ @@ -51,7 +51,7 @@ BADGES_KATALOG = { "dialog_profi": { "id": "dialog_profi", "icon": "💬", - "name": "Dialog-Profi", + "name": "Zertifikat: Interaktive I/O & String-Interpolation", "desc": "Grundlagen 03 abgeschlossen (Interaktive Ein-/Ausgabe mit f-Strings).", "xp_bonus": 75, "kriterium": ["lehrpfad_1_grundlagen/03_ein_und_ausgabe"] @@ -59,32 +59,32 @@ BADGES_KATALOG = { "weichenstellerin": { "id": "weichenstellerin", "icon": "🚦", - "name": "Weichenstellerin", - "desc": "Grundlagen 04 abgeschlossen (Bedingungslogik & Schaltjahre gemeistert).", + "name": "Zertifikat: Verzweigungslogik & Boolesche Algebra", + "desc": "Grundlagen 04 abgeschlossen (Bedingungslogik & Kontrollfluss gemeistert).", "xp_bonus": 75, "kriterium": ["lehrpfad_1_grundlagen/04_verzweigungen_und_bedingungen"] }, "schleifen_dompteurin": { "id": "schleifen_dompteurin", "icon": "🎡", - "name": "Schleifen-Dompteurin", - "desc": "Grundlagen 05 abgeschlossen (While- & For-Schleifen gebändigt).", + "name": "Zertifikat: Iterationslogik & Sequenzverarbeitung", + "desc": "Grundlagen 05 abgeschlossen (While- & For-Schleifen gemeistert).", "xp_bonus": 100, "kriterium": ["lehrpfad_1_grundlagen/05_schleifen_und_wiederholungen"] }, "funktions_zauberin": { "id": "funktions_zauberin", - "icon": "🪄", - "name": "Funktions-Zauberin", - "desc": "Grundlagen 06 abgeschlossen (Eigene Funktionen & Module gebaut).", + "icon": "📐", + "name": "Zertifikat: Modulare Funktionsarchitektur", + "desc": "Grundlagen 06 abgeschlossen (Modulare Funktionen & Parameterübergabe).", "xp_bonus": 100, "kriterium": ["lehrpfad_1_grundlagen/06_funktionen_und_module"] }, "listen_archivarin": { "id": "listen_archivarin", "icon": "📋", - "name": "Listen-Archivarin", - "desc": "Grundlagen 07 & 08 abgeschlossen (Sequenzen & Strings analysiert).", + "name": "Zertifikat: Datenstrukturen: Listen & Slicing", + "desc": "Grundlagen 07 & 08 abgeschlossen (Sequenzen, Listen & Slicing analysiert).", "xp_bonus": 120, "kriterium": [ "lehrpfad_1_grundlagen/07_listen_und_sequenzen", @@ -94,8 +94,8 @@ BADGES_KATALOG = { "daten_strategin": { "id": "daten_strategin", "icon": "🗃️", - "name": "Daten-Strategin", - "desc": "Grundlagen 09 & 10 abgeschlossen (Dicts, Sets & Comprehensions gemeistert).", + "name": "Zertifikat: Assoziative Arrays: Dicts & Sets", + "desc": "Grundlagen 09 & 10 abgeschlossen (Dictionaries, Sets & Hash-Lookup O(1)).", "xp_bonus": 150, "kriterium": [ "lehrpfad_1_grundlagen/09_dictionaries_und_sets", @@ -105,16 +105,16 @@ BADGES_KATALOG = { "bug_jaegerin": { "id": "bug_jaegerin", "icon": "🔍", - "name": "Bug-Jägerin", - "desc": "Kapitel 00 gelöst (Alle Fehler-Bugs im Warm-up aufgespürt).", + "name": "Zertifikat: Systematisches Code-Debugging", + "desc": "Warm-up abgeschlossen (Fehleranalyse & Traceback-Debugging).", "xp_bonus": 100, "kriterium": ["lehrpfad_2_oop_einstieg/00_fehlersuche_und_grundlagen"] }, "erste_architektin": { "id": "erste_architektin", "icon": "🏗️", - "name": "Erste Architektin", - "desc": "Kapitel 01 bis 03 gelöst (Erste OOP-Klassen, Konstruktoren & Methoden).", + "name": "Zertifikat: OOP-Grundlagen: Klassen & Kapselung", + "desc": "OOP 01 bis 03 gelöst (Klassendefinition, Konstruktoren & Instanzmethoden).", "xp_bonus": 200, "kriterium": [ "lehrpfad_2_oop_einstieg/01_einstieg_klassen", @@ -124,33 +124,33 @@ BADGES_KATALOG = { }, "tamagotchi_mama": { "id": "tamagotchi_mama", - "icon": "🥚", - "name": "Tamagotchi-Mama", - "desc": "Kapitel 06 Mini-Projekt abgeschlossen (Ein lebendiges Haustier gebaut).", + "icon": "📦", + "name": "Zertifikat: State-Machine & Domänenmodellierung", + "desc": "OOP Mini-Projekt abgeschlossen (Zustandsverwaltung & Objektinteraktion).", "xp_bonus": 250, "kriterium": ["lehrpfad_2_oop_einstieg/06_abschlussprojekt_tamagotchi"] }, "zeit_reisende": { "id": "zeit_reisende", "icon": "🌿", - "name": "Git-Zeitreisende", - "desc": "Kapitel 04c Git-Versionskontrolle verstanden & Spielstände gesichert.", + "name": "Zertifikat: String-Repräsentation & Dunder-Methoden", + "desc": "OOP 04 gelöst (Spezielle Methoden __str__, __repr__ implementiert).", "xp_bonus": 100, "kriterium": ["lehrpfad_2_oop_einstieg/04_str_und_darstellung"] }, "qualitaets_garantin": { "id": "qualitaets_garantin", "icon": "🧪", - "name": "TDD-Qualitätsgarantin", - "desc": "Kapitel 09 abgeschlossen (Eigene professionelle Unit Tests geschrieben).", + "name": "Zertifikat: Automatisierte Testsuites & TDD", + "desc": "OOP 09 abgeschlossen (Eigene professionelle Unit Tests mit Assertions).", "xp_bonus": 200, "kriterium": ["lehrpfad_3_fortgeschrittenes_oop/09_eigene_unit_tests_schreiben"] }, "oop_grossmeisterin": { "id": "oop_grossmeisterin", "icon": "🧬", - "name": "Vererbungs-Koryphäe", - "desc": "Kapitel 10 bis 12 abgeschlossen (Vererbung, Polymorphie & Exceptions).", + "name": "Zertifikat: Fortgeschrittene OOP & Robustes Error-Handling", + "desc": "OOP 10 bis 12 abgeschlossen (Vererbung, Polymorphie & Exceptions).", "xp_bonus": 300, "kriterium": [ "lehrpfad_3_fortgeschrittenes_oop/10_vererbung_und_super", @@ -161,8 +161,8 @@ BADGES_KATALOG = { "software_architektin": { "id": "software_architektin", "icon": "🏆", - "name": "Meister-Entwicklerin", - "desc": "Kapitel 16 Master-Projekt abgeschlossen & vollwertige Desktop-App gebaut!", + "name": "Zertifikat: Master-Projekt Software Engineering", + "desc": "Master-Projekt abgeschlossen & vollwertige Anwendungsarchitektur gebaut!", "xp_bonus": 500, "kriterium": ["lehrpfad_3_fortgeschrittenes_oop/16_master_abschlussprojekt"] } @@ -178,11 +178,11 @@ class GamificationManager: def _default_state(self) -> Dict[str, Any]: return { - "spieler_name": "Python-Entwicklerin", + "spieler_name": "IT-Entwickler", "xp": 0, "level": 1, - "titel": "Code-Küken 🐣", - "rang": "Bronze I", + "titel": "Junior IT-Trainee I: Syntax & Basistypen", + "rang": "Stufe 1", "geloeste_kapitel": [], "bestandene_tests": 0, "freigeschaltete_badges": [], diff --git a/workspace.html b/workspace.html index a254cc8..5b650bf 100644 --- a/workspace.html +++ b/workspace.html @@ -47,20 +47,24 @@ .workspace-body { flex: 1; - display: grid; - grid-template-columns: 42% 58%; + display: flex; + flex-direction: row; height: calc(100vh - 52px); overflow: hidden; + position: relative; } .pane-left { + width: 40%; + min-width: 280px; + max-width: 65%; background: #ffffff; color: var(--text-main); display: flex; flex-direction: column; - border-right: 2px solid #e2e8f0; height: 100%; overflow: hidden; + flex-shrink: 0; } .pane-tabs { @@ -101,19 +105,21 @@ } .pane-right { + flex: 1; + min-width: 360px; display: flex; flex-direction: column; height: 100%; - background: #0b0f19; + background: #090d16; overflow: hidden; } .editor-wrapper { - flex: 1; height: 65%; - min-height: 200px; + min-height: 140px; position: relative; - background: #0b0f19; + background: #090d16; + flex-shrink: 0; } #monaco-editor-container { @@ -122,12 +128,23 @@ } .terminal-wrapper { - height: 35%; - min-height: 140px; + flex: 1; + min-height: 80px; background: #060911; - border-top: 2px solid rgba(255, 255, 255, 0.08); display: flex; flex-direction: column; + overflow: hidden; + transition: height 0.15s ease; + } + + .terminal-wrapper.collapsed { + flex: none !important; + height: 34px !important; + min-height: 34px !important; + } + + .terminal-wrapper.collapsed #workspace-terminal-output { + display: none; } .terminal-header { @@ -286,16 +303,28 @@ + +
+ -
-
+
+
-
+ +
+ +
- 🖥️ Terminal / Test-Ausgabe - +
+ 🖥️ Terminal / Test-Ausgabe + +
+
+ + +
Klicke oben auf '▶ Code ausführen' (Strg+Enter) oder '🧪 Tests starten' (Strg+Shift+Enter)...
@@ -708,7 +737,206 @@ } }; - document.addEventListener("DOMContentLoaded", loadWorkspaceData); + /* --- Resizable Splitters with State Persistence & Double-Click Reset --- */ + function initSplitResizers() { + const layout = document.querySelector(".workspace-body"); + const leftPane = document.querySelector(".pane-left"); + const gutterCol = document.getElementById("gutter-col"); + const editorWrap = document.getElementById("editor-wrapper"); + const gutterRow = document.getElementById("gutter-row"); + const termWrap = document.getElementById("terminal-wrapper"); + const btnCollapseTerm = document.getElementById("btn-toggle-terminal-collapse"); + + if (!layout || !leftPane || !gutterCol || !editorWrap || !gutterRow) return; + + // 1. Aus localStorage wiederherstellen + const savedLeft = localStorage.getItem("itp_split_x"); + const savedEditor = localStorage.getItem("itp_split_y"); + if (savedLeft && window.innerWidth >= 900) { + leftPane.style.width = savedLeft + "%"; + } + if (savedEditor) { + editorWrap.style.height = savedEditor + "%"; + } + + // 2. Horizontaler Resizer (Didaktik links vs. Workspace rechts) + gutterCol.addEventListener("mousedown", (e) => { + e.preventDefault(); + gutterCol.classList.add("dragging"); + document.body.style.cursor = "col-resize"; + document.body.style.userSelect = "none"; + + let animFrame = null; + function onMouseMove(moveEvent) { + if (animFrame) cancelAnimationFrame(animFrame); + animFrame = requestAnimationFrame(() => { + const containerWidth = layout.getBoundingClientRect().width; + let percent = (moveEvent.clientX / containerWidth) * 100; + if (percent < 22) percent = 22; + if (percent > 65) percent = 65; + leftPane.style.width = percent.toFixed(2) + "%"; + if (window.monacoEditor) window.monacoEditor.layout(); + }); + } + + function onMouseUp() { + gutterCol.classList.remove("dragging"); + document.body.style.cursor = ""; + document.body.style.userSelect = ""; + localStorage.setItem("itp_split_x", parseFloat(leftPane.style.width)); + window.removeEventListener("mousemove", onMouseMove); + window.removeEventListener("mouseup", onMouseUp); + if (window.monacoEditor) window.monacoEditor.layout(); + } + + window.addEventListener("mousemove", onMouseMove); + window.addEventListener("mouseup", onMouseUp); + }); + + // Doppelklick auf Spalten-Splitter: Zurücksetzen auf 40/60 + gutterCol.addEventListener("dblclick", () => { + leftPane.style.width = "40%"; + localStorage.setItem("itp_split_x", "40"); + if (window.monacoEditor) window.monacoEditor.layout(); + }); + + // 3. Vertikaler Resizer (Editor oben vs. Terminal unten) + gutterRow.addEventListener("mousedown", (e) => { + e.preventDefault(); + gutterRow.classList.add("dragging"); + document.body.style.cursor = "row-resize"; + document.body.style.userSelect = "none"; + + const rightPane = document.getElementById("pane-right"); + let animFrame = null; + + function onMouseMove(moveEvent) { + if (animFrame) cancelAnimationFrame(animFrame); + animFrame = requestAnimationFrame(() => { + const rightRect = rightPane.getBoundingClientRect(); + const relY = moveEvent.clientY - rightRect.top; + let percent = (relY / rightRect.height) * 100; + if (percent < 20) percent = 20; + if (percent > 80) percent = 80; + editorWrap.style.height = percent.toFixed(2) + "%"; + if (window.monacoEditor) window.monacoEditor.layout(); + }); + } + + function onMouseUp() { + gutterRow.classList.remove("dragging"); + document.body.style.cursor = ""; + document.body.style.userSelect = ""; + localStorage.setItem("itp_split_y", parseFloat(editorWrap.style.height)); + window.removeEventListener("mousemove", onMouseMove); + window.removeEventListener("mouseup", onMouseUp); + if (window.monacoEditor) window.monacoEditor.layout(); + } + + window.addEventListener("mousemove", onMouseMove); + window.addEventListener("mouseup", onMouseUp); + }); + + // Doppelklick auf Zeilen-Splitter: Zurücksetzen auf 65/35 + gutterRow.addEventListener("dblclick", () => { + editorWrap.style.height = "65%"; + localStorage.setItem("itp_split_y", "65"); + if (window.monacoEditor) window.monacoEditor.layout(); + }); + + // 4. Terminal Einklappen / Ausklappen + if (btnCollapseTerm && termWrap) { + btnCollapseTerm.addEventListener("click", () => { + const isCollapsed = termWrap.classList.toggle("collapsed"); + btnCollapseTerm.innerText = isCollapsed ? "▲" : "▼"; + btnCollapseTerm.title = isCollapsed ? "Panel ausklappen" : "Panel einklappen"; + if (isCollapsed) { + editorWrap.style.height = "calc(100% - 34px)"; + } else { + const savedY = localStorage.getItem("itp_split_y") || "65"; + editorWrap.style.height = savedY + "%"; + } + setTimeout(() => { + if (window.monacoEditor) window.monacoEditor.layout(); + }, 160); + }); + } + } + + /* --- Tastatur-Ergonomie & Global Shortcuts --- */ + function initWorkspaceShortcuts() { + let tabTrapped = true; + + window.addEventListener("keydown", (e) => { + // 1. Code ausführen: Ctrl+Enter / Cmd+Enter + if ((e.ctrlKey || e.metaKey) && !e.shiftKey && e.key === "Enter") { + e.preventDefault(); + const btnRun = document.getElementById("btn-run-code"); + if (btnRun) btnRun.click(); + return; + } + + // 2. Tests starten: Ctrl+Shift+Enter / Cmd+Shift+Enter + if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === "Enter") { + e.preventDefault(); + const btnTests = document.getElementById("btn-run-tests"); + if (btnTests) btnTests.click(); + return; + } + + // 3. Tipps & Lösung: Ctrl+H / Cmd+H + if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "h") { + e.preventDefault(); + const btnHints = document.getElementById("btn-show-hints"); + if (btnHints) btnHints.click(); + return; + } + + // 4. Fokus in Editor: Ctrl+1 / Cmd+1 + if ((e.ctrlKey || e.metaKey) && e.key === "1") { + e.preventDefault(); + if (window.monacoEditor) window.monacoEditor.focus(); + return; + } + + // 5. Fokus auf Theorie: Ctrl+2 / Cmd+2 + if ((e.ctrlKey || e.metaKey) && e.key === "2") { + e.preventDefault(); + const tabTheorie = document.querySelector(".pane-tab-btn"); + if (tabTheorie) tabTheorie.click(); + document.getElementById("tab-content-theorie")?.focus(); + return; + } + + // 6. Fokus ins Terminal: Ctrl+` / Cmd+` + if ((e.ctrlKey || e.metaKey) && e.key === "`") { + e.preventDefault(); + document.getElementById("workspace-terminal-output")?.focus(); + return; + } + + // 7. Nächstes Kapitel: Ctrl+Alt+ArrowRight + if (e.ctrlKey && e.altKey && e.key === "ArrowRight") { + e.preventDefault(); + document.getElementById("btn-topbar-next-chapter")?.click(); + return; + } + + // 8. Tab-Trapping im Editor lösen mit Esc oder Ctrl+M (WCAG 2.1) + if (e.key === "Escape" || ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "m")) { + if (window.monaco && window.monaco.editor) { + tabTrapped = !tabTrapped; + window.monaco.editor.setTabFocusMode(!tabTrapped); + } + } + }); + } + + document.addEventListener("DOMContentLoaded", () => { + loadWorkspaceData(); + initSplitResizers(); + initWorkspaceShortcuts(); + });