programmierhub/public/index.html
Stefan Franke 502131f07c VR ProgrammierHub — begehbarer 3D-Lernraum für hybride Programmier-Lehre
Erste lauffähige Fassung: 3D-Raum, Echtzeit-Multiplayer (WebSocket),
Python-IDE via Pyodide im Browser, eigener Arbeitsplatz mit Live-Code-Monitor.
Modularer, self-hostbarer Aufbau (Three.js + Node/Express + ws), ohne CDN-
Abhängigkeit zur Laufzeit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 13:35:11 +00:00

93 lines
3.4 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>VR ProgrammierHub</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="app"></div>
<div id="hud">
<div><b>VR ProgrammierHub</b></div>
<div>Bewegen: <b>W A S D</b> · Rennen <b>Shift</b></div>
<div>Umschauen: <b>Maus</b> (Klick fängt, Esc frei) · Pfeiltasten</div>
<div>Netz: <span id="net"></span> · Online: <b id="count">0</b></div>
<div>FPS: <span id="fps"></span> · Init: <span id="dbg">lädt…</span></div>
</div>
<div id="overlay">
<h1>VR <span>ProgrammierHub</span></h1>
<p>Gib dir einen Namen und betritt den Raum. Andere im selben Raum siehst du live als Avatar.
<span class="key">W A S D</span> laufen; zum Umschauen ins Bild klicken — die Maus wird gefangen und dreht die Blickrichtung frei, auch beim Laufen (<span class="key">Esc</span> gibt sie frei). Ohne Maus: <span class="key">← →</span> zum Drehen.</p>
<div class="row">
<input id="name" placeholder="Dein Name" maxlength="24" autocomplete="off">
<select id="role"><option value="student">Studierende:r</option><option value="teacher">Lehrperson</option></select>
</div>
<button id="startbtn">Raum betreten</button>
</div>
<div id="toolbar">
<button id="ideBtn">💻 Code</button>
<button id="helpBtn">✋ Hilfe anfragen</button>
</div>
<div id="sithint">🪑 An deinen Platz setzen — <span class="key">E</span></div>
<div id="ide">
<div id="ide-bar">
<span id="ide-title">💻 meine_uebung.py</span>
<span id="ide-status"></span>
<span class="spacer"></span>
<button id="ide-run">▶ Ausführen (Strg+Enter)</button>
<button id="ide-close">✕ Schließen (Esc)</button>
</div>
<textarea id="ide-editor" spellcheck="false"># Willkommen im ProgrammierHub 🐍
# Schreib Python und klick auf „Ausführen" (oder Strg+Enter).
class Punkt:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Punkt({self.x}, {self.y})"
for i in range(4):
print(i, "->", Punkt(i, i * i))
</textarea>
<pre id="ide-output"></pre>
</div>
<div id="err"></div>
<!-- Sicherheitsnetz: fängt Ladefehler global ab, unabhängig vom Modul. -->
<script>
window.__hubReady = false;
window.addEventListener('error', function (e) {
var b = document.getElementById('err'); if (!b) return;
b.style.display = 'block';
b.textContent = '⚠ Fehler (bitte an Claude weitergeben):\n' + (e.message || e.error || 'Ladefehler (Script/Modul)') + '\n' + (e.filename || '') + (e.lineno ? ':' + e.lineno : '');
});
window.addEventListener('unhandledrejection', function (e) {
var b = document.getElementById('err'); if (!b) return;
b.style.display = 'block';
b.textContent = '⚠ Promise-Fehler:\n' + (e.reason && (e.reason.stack || e.reason.message || e.reason));
});
// Watchdog: falls das Modul nicht startet (z. B. Netzwerk), klare Meldung statt stummem Blank.
setTimeout(function () {
if (window.__hubReady) return;
var b = document.getElementById('err'); if (!b) return;
b.style.display = 'block';
b.textContent = '⚠ Das 3D-Modul wurde nicht geladen (Netzwerk/Script-Fehler).\nBitte hart neu laden: Strg+Shift+R';
}, 4000);
</script>
<script type="importmap">
{ "imports": { "three": "/vendor/three.module.js" } }
</script>
<script type="module" src="/js/main.js"></script>
</body>
</html>