Plattformweit: Idle-Pause-Overlay nach 3 min Untaetigkeit mit "Weiter"-Button

live-client.js (laedt in JEDER Sim): nach 3 min ohne Interaktion sichtbare Pause
(friert Aktivzeit ein -> Idle zaehlt nicht als Arbeitszeit), Weiterarbeiten per
Klick. Heartbeat meldet __paused/__idlePaused. Kein Sim-Code noetig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 23:32:50 +02:00
parent 2386756a20
commit 48971b4588
+47 -4
View File
@@ -55,7 +55,8 @@
// Großzügig (5 min): Lesen und Nachdenken sollen NICHT als „weg" gelten.
// Scrollen/Mausbewegung zählen ohnehin als Interaktion (siehe EVENTS), und
// echtes Weg-Sein (anderer Tab) wird über die Tab-Sichtbarkeit ausgeblendet.
var IDLE_THRESHOLD_MS = 300000; // 5 min ohne jede Regung → idle
var IDLE_THRESHOLD_MS = 300000; // 5 min ohne jede Regung → idle (Backstop)
var IDLE_PAUSE_MS = 180000; // 3 min → sichtbare Pause mit „Weiter"-Button
var MAX_SESSION_MS = 7200000; // 120 min — Plattform-Hardcap
var TICK_MS = 1000; // Auflösung der activeMs-Akkumulation
var sessionStartedAt = Date.now();
@@ -64,6 +65,7 @@
var lastTickAt = Date.now();
var sessionCapped = false;
var paused = false; // Sim-Pause: friert activeMs ein (opt-in)
var idleActive = false; // sichtbare Idle-Pause aktiv (Weiter-Overlay)
function onInteraction() {
lastInteractionAt = Date.now();
@@ -81,9 +83,9 @@
var now = Date.now();
var delta = now - lastTickAt;
lastTickAt = now;
// Spiel pausiert (Sim hat setPaused(true) gerufen) → Zeit steht, nicht zählen.
// Pausiert (Sim-setPaused ODER sichtbare Idle-Pause) → Zeit steht, nicht zählen.
// lastTickAt ist schon aktualisiert, daher springt activeMs beim Fortsetzen nicht.
if (paused) return;
if (paused || idleActive) return;
// Tab nicht sichtbar (anderer Tab, minimiert) → echtes Weg-Sein, nicht zählen.
// Denkpausen bei SICHTBAREM Tab bleiben über das großzügige Idle-Fenster erhalten.
if (typeof document !== 'undefined' && document.hidden) return;
@@ -91,6 +93,14 @@
// anrechnen, der Rest war definitiv nicht aktive Spielzeit.
if (delta > TICK_MS * 3) delta = 0;
var idleFor = now - lastInteractionAt;
// Nach 3 min Untätigkeit: sichtbare Pause einblenden (friert die Zeit ein,
// damit Idle nicht als Arbeitszeit zählt). Nicht im Spectator-Modus und
// nicht, während die Lehrperson ohnehin pausiert hat.
if (idleFor >= IDLE_PAUSE_MS && !isSpectator()
&& !(pauseOverlay && pauseOverlay.style.display !== 'none')) {
showIdlePause();
return;
}
if (idleFor < IDLE_THRESHOLD_MS) {
activeMs += delta;
if (activeMs >= MAX_SESSION_MS && !sessionCapped) {
@@ -138,7 +148,8 @@
state.__lastInteractionAt = lastInteractionAt;
state.__sessionStartedAt = sessionStartedAt;
state.__capped = sessionCapped;
state.__paused = paused;
state.__paused = paused || idleActive;
state.__idlePaused = idleActive;
fetch(apiBase + '/live.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -175,6 +186,38 @@
.catch(function () {});
}
// --- Idle-Pause (plattformweit, für JEDE Sim) ---
// Nach längerer Untätigkeit blenden wir eine freundliche Pause mit „Weiter"-
// Button ein. Solange sie sichtbar ist, steht die Aktivzeit (idleActive) —
// die Pause zählt also NICHT als Arbeitszeit. Der Lernende kann jederzeit
// per Klick weitermachen. Kein Sim-Code nötig (dieses Asset lädt überall).
var idleOverlay = null;
function ensureIdleOverlay() {
if (idleOverlay) return idleOverlay;
var el = document.createElement('div');
el.id = 'ggs-idle-overlay';
el.style.cssText = 'position:fixed;inset:0;background:rgba(31,75,55,.82);color:#fff;z-index:99999;display:none;flex-direction:column;align-items:center;justify-content:center;font-family:Inter,-apple-system,system-ui,sans-serif;text-align:center;padding:2rem;backdrop-filter:blur(3px);cursor:pointer';
el.innerHTML = '<div style="font-size:3.6rem;margin-bottom:.6rem">😴</div>'
+ '<div style="font-size:1.55rem;font-weight:700;margin-bottom:.5rem">Kurze Pause</div>'
+ '<div style="font-size:1rem;font-weight:500;opacity:.9;max-width:440px;line-height:1.45;margin-bottom:1.5rem">Du warst eine Weile ganz ruhig — ich habe die Zeit angehalten, damit die Pause nicht als Arbeitszeit zählt. Klick auf „Weiter", wenn du weitermachen möchtest.</div>'
+ '<button id="ggs-idle-resume" style="font-size:1.05rem;font-weight:700;padding:.75rem 2.2rem;border:none;border-radius:10px;background:#fff;color:#1f4b37;cursor:pointer;box-shadow:0 3px 10px rgba(0,0,0,.25)">Weiter</button>';
document.body.appendChild(el);
el.addEventListener('click', resumeFromIdle);
idleOverlay = el;
return el;
}
function showIdlePause() {
if (idleActive) return;
idleActive = true;
ensureIdleOverlay().style.display = 'flex';
}
function resumeFromIdle() {
idleActive = false;
lastInteractionAt = Date.now();
lastTickAt = Date.now(); // kein Zeitsprung beim Fortsetzen
if (idleOverlay) idleOverlay.style.display = 'none';
}
// --- Spectator-Mode für Lehrkräfte ---
// ?view=teacher schaltet Input ab und blendet ein dezentes Banner ein.
function isSpectator() {