From 996231af6e652026ea36d9f3633382eb9d0aaa60 Mon Sep 17 00:00:00 2001 From: Dogfather Date: Thu, 8 Oct 2026 01:31:24 +0200 Subject: [PATCH] Reaction: Material statt Kaesten, Spende in der Mitte, Gaeste werden gefragt Nach Filipes sieben Bildschirmfotos vom 07.10.2026. Sieben Wuensche, und einer davon ("alles viel hochwertiger") betrifft alle anderen -- deshalb steht das Fundament zuerst und nicht am Ende. DAS AUSSEHEN HAT EINEN WERKSTOFF. Jede Flaeche sagt jetzt, ob sie oben oder unten liegt: Was obenauf liegt, bekommt oben eine helle Kante und darunter einen Schatten; was vertieft ist, genau umgekehrt. Dazwischen gibt es nichts mehr. Vorher stand im ganzen Pult zweimal eine Lichtkante und sonst nirgends -- genau deshalb sahen diese zwei Stellen gut aus und alles daneben danebengestellt. Dazu: jeder Knopf senkt sich beim Druecken, jedes Feld ist ein Loch und kein Kasten, und wer mit der Tastatur bedient, wird zum ersten Mal ueberhaupt gesehen. UND EIN FUND DABEI: Die drei Kacheln des Regieplatzes hiessen in der HTML anders als im Stilblatt (kachel--laeuft gegen regiekachel--laeuft, regieregiekachel__kopf gegen regiekachel__kopf). Die Aufteilung "links was laeuft, rechts was man eintraegt" hat damit NIE gegriffen -- und mess-reaktion hat den falschen Zustand gemessen und gruen gemeldet, weil sie gegen die HTML geschrieben war. DIE SPENDENKARTE STEHT IN DER MITTE und kommt aus der Tiefe nach vorn. Das dreht eine Entscheidung vom 28.09. um ("sie deckt nie die Mitte zu"); die Begruendung war richtig und ist es noch -- sie schuetzt das Video, und Filipe sieht die Spende als das, was in dem Augenblick zaehlt. Der alte Platz bleibt einen Knopf weit weg und gilt fuer Saal UND OBS-Tafel. Die Karte besteht dafuer aus zwei Teilen: `opacity` unter 1 zwingt ein Element auf `transform-style: flat`, eine Karte mit Deckkraft-Uebergang waere also genau waehrend des Hereinkommens flach und spraenge am Ende in einem Bild in die Tiefe. FERTIGE SAETZE GEHEN SOFORT INS BILD. Der Browser schickt nur die Kennung, den Text holt der Server -- deshalb darf hier die Bestaetigung entfallen und beim freien Text nicht. Zwei Schranken bleiben: sofort nur mit einem der vier Knopfbetraege (Filipes Ansage galt dem SATZ, nicht der Summe), und GEZEIGT IST NICHT GEBUCHT -- die Dogen gibt es erst nach dem Tipp der Leitung, sonst stellt sich jeder seine eigene Waehrung aus. WER GESPENDET HAT, GLAENZT IM CHAT -- je nach Hoehe. Die Dauer haengt an der Stufe und nicht an einer zweiten Leiter; 0 heisst "gar nicht". GAESTE WERDEN GEFRAGT, NICHT GEHOLT. Vorher sprang die Kamera eines Zuschauers an, sobald jemand "Dazuholen" drueckte -- er hat es daran gemerkt, dass er sich selbst sah. Jetzt gibt es einen Vorraum: eigenes Bild, Pegel, Kamera und Mikro vorwaehlen, Geraete waehlen, dann "Dabei sein" oder "Lieber nicht". Zwei Minuten Frist, und sie verfaellt SICHTBAR -- eine verschwundene Zeile sieht fuer den Host aus wie ein Verklicker. DAS MISCHPULT. Hier stand fest `echoCancellation: true, noiseSuppression: true`. Fuer ein Laptopmikrofon richtig, ueber einem RODECaster falsch: Das Pult hat Gate, Kompressor und EQ schon gemacht, und die Echo-Unterdrueckung legt sich danach als zweite Automatik darueber und duckt, sobald von der Gegenseite Ton kommt -- genau das Pumpen vom 14.09. Jetzt: Geraetewahl fuer Mikro, Kamera und Ausgabe (auch fuer Gaeste), ein Pultmodus, und darunter GEMESSEN, was wirklich ankommt. Mit und ohne Pult. DAS ZWEITE VIDEO war nie gesperrt -- es war nur unsichtbar. Jetzt stehen Vorschaubild, Titel und die gemerkte Stelle da, "Jetzt wechseln" auch von hier aus, und aus der Warteschlange legt ein Knopf eines bereit. UND EIN FUND AUF DEM BILDSCHIRMFOTO: Das Band "Ton einschalten" lag auf derselben Hoehe wie die Senderleiste. Bei vier Knoepfen reichte die Leiste bis zur Mitte -- vom Mikro-Knopf war nur noch ein "t" zu sehen. Das war schon vorher so; der fuenfte Knopf hat es sichtbar gemacht. Dieselbe Lehre wie am 06.09.: keine neue Zahl suchen, sondern die Begegnung unmoeglich machen. mess-reaktion misst das jetzt als UEBERLAPPUNG. GEPRUEFT: pruef-reaktion 423 -> 478 (0 Fehler), pruef-spenden 172 -> 215 (0), pruef-reaktion-schmal 44 (0), pruef-tippziele 13 (0), dazu gruen: pruef-css-klassen, pruef-alle-wege, pruef-haus-trennung, pruef-agentur, pruef-deutsche-texte. Keine Zahl ist gefallen. Drei Doppelungen hat erst das Pruefen gefunden: `satz_unbekannt` gab es zweimal (Muenzsatz), `saetze` im Spendenstand war belegt (ebenfalls Muenzsaetze), und `glanz_minuten` waere in einem frischen Haus immer 0 geblieben -- die Spalte kam per ALTER TABLE, und das laeuft VOR dem Anlegen der Werksstufen. Co-Authored-By: Claude Opus 5 --- server/mess-buehne.mjs | 2 +- server/mess-quer.mjs | 7 + server/mess-reaktion.mjs | 364 ++++++- server/pruef-reaktion-schmal.mjs | 16 + server/pruef-reaktion.mjs | 356 ++++++- server/pruef-spenden.mjs | 303 ++++++ server/reaktion-tabellen.js | 61 +- server/spenden-tabellen.js | 184 +++- server/workspace-reaktion.js | 292 +++++- server/workspace-spenden.js | 344 ++++++- workspace/anleitung.html | 28 +- workspace/anruf-probe.html | 10 +- workspace/app.webmanifest | 8 +- workspace/assets/css/reaktion.css | 1183 ++++++++++++++++++++++- workspace/assets/css/spendenkarte.css | 395 +++++++- workspace/assets/js/meldung.js | 27 + workspace/assets/js/reaktion.js | 1288 ++++++++++++++++++++++++- workspace/assets/js/spendenkarte.js | 60 +- workspace/assets/js/tafel.js | 46 +- workspace/aufgaben.html | 38 +- workspace/automation.html | 32 +- workspace/befinden.html | 34 +- workspace/bereich.html | 48 +- workspace/bewerben.html | 38 +- workspace/bewerbungen.html | 36 +- workspace/buehne.html | 6 +- workspace/calls.html | 34 +- workspace/chat.html | 42 +- workspace/content.html | 34 +- workspace/crew-index.html | 12 +- workspace/crew.webmanifest | 8 +- workspace/dateien.html | 34 +- workspace/entwicklung.html | 36 +- workspace/hilfe.html | 30 +- workspace/index.html | 10 +- workspace/kalender.html | 34 +- workspace/leistung.html | 32 +- workspace/manager-ziele.html | 28 +- workspace/material.html | 32 +- workspace/notizen.html | 28 +- workspace/personen.html | 32 +- workspace/profil.html | 36 +- workspace/reaktion.html | 313 +++++- workspace/rechte.html | 34 +- workspace/report.html | 36 +- workspace/scouting.html | 32 +- workspace/start.html | 38 +- workspace/startcheck.html | 34 +- workspace/steckbrief.html | 36 +- workspace/support.html | 28 +- workspace/tafel.html | 47 +- workspace/talente.html | 36 +- workspace/team.html | 32 +- workspace/teamlage.html | 34 +- workspace/teilen.html | 30 +- workspace/treff-moderation.html | 32 +- workspace/treff-regeln.html | 32 +- workspace/uebersicht.html | 32 +- workspace/unsere-seiten.html | 30 +- workspace/unterstuetzen.html | 32 +- workspace/werdegang.html | 32 +- workspace/willkommen.html | 2 +- workspace/wissen.html | 34 +- 63 files changed, 5783 insertions(+), 841 deletions(-) diff --git a/server/mess-buehne.mjs b/server/mess-buehne.mjs index 4e5f839b..4602d761 100644 --- a/server/mess-buehne.mjs +++ b/server/mess-buehne.mjs @@ -335,7 +335,7 @@ await roh("/workspace/api/spenden", { rumpf: { dogen_mille: 2500, name: "Nachtfalter", gruss: "Gruss an die Buehne" }, }); await tafelSeite.waitForFunction( - () => document.querySelector('.spendenkarte[data-da="ja"]'), null, { timeout: 12000 }, + () => document.querySelector('.spendenkarte-auftritt[data-da="ja"] .spendenkarte'), null, { timeout: 12000 }, ).catch(() => {}); await tafelSeite.waitForTimeout(600); const karte = await tafelSeite.evaluate(() => { diff --git a/server/mess-quer.mjs b/server/mess-quer.mjs index 61f35d17..7a6f0333 100644 --- a/server/mess-quer.mjs +++ b/server/mess-quer.mjs @@ -269,6 +269,13 @@ for (const stufe of ["vorbereitung", "live"]) { await roh("/workspace/api/reaktion/gast", { method: "POST", keks: k.admin, rumpf: { person_id: IDS.gast2 }, }); +/* ZUSAGEN GEHOERT SEIT DEM 08.10.2026 DAZU. Ein Eingeladener steht + noch NICHT auf der Buehne -- ohne diesen zweiten Aufruf maesse + dieses Werkzeug einen Saal mit einem Kamerafenster statt zweien, + und zwar ohne einen einzigen Fehler zu melden. */ +await roh("/workspace/api/reaktion/gast/antwort", { + method: "POST", keks: k.gast2, rumpf: { ja: true, kamera: true, mikro: true }, +}); /* Und ein paar Worte im Chat -- eine leere Schiene kann nicht ueberlaufen, und genau das waere ein gruener Haken ohne Inhalt. */ for (const [wer, text] of REDEN) { diff --git a/server/mess-reaktion.mjs b/server/mess-reaktion.mjs index 72e5a55f..7cd66d58 100644 --- a/server/mess-reaktion.mjs +++ b/server/mess-reaktion.mjs @@ -277,6 +277,10 @@ for (const [wer, text] of REDEN) { console.log("\n=== LIVE ==="); await roh("/workspace/api/reaktion/stand", { method: "POST", keks: k.admin, rumpf: { stand: "live" } }); await roh("/workspace/api/reaktion/gast", { method: "POST", keks: k.admin, rumpf: { person_id: IDS.gast2 } }); +/* Und die Zusage: Seit dem 08.10.2026 wird GEFRAGT, nicht geholt -- + wer nur gefragt ist, sendet noch nicht. */ +await roh("/workspace/api/reaktion/gast/antwort", + { method: "POST", keks: k.gast2, rumpf: { ja: true, kamera: true, mikro: true } }); /* ==== BEIDE SEITEN GLEICHZEITIG ==================================== Der Host sendet, die Zuschauerin empfaengt -- das geht nur, wenn @@ -785,9 +789,9 @@ await host.seite.waitForTimeout(900); Regiespalte stehen sie UNTEREINANDER -- das ist seit dem Umbau „Regie links" der Sinn, nicht ein Fehler. Gefragt ist deshalb: Gibt es alle drei, und hat jede Inhalt? */ - laeuft: k(".regiekachel.kachel--laeuft"), - sendung: k(".regiekachel.kachel--sendung"), - bereit: k(".regiekachel.kachel--bereit"), + laeuft: k(".regiekachel.regiekachel--laeuft"), + sendung: k(".regiekachel.regiekachel--sendung"), + bereit: k(".regiekachel.regiekachel--bereit"), leiste: k(".transport"), spiel: k("#v-spiel"), /* Laeuft etwas seitlich hinaus? `scrollWidth` groesser als die @@ -825,7 +829,7 @@ await host.seite.waitForTimeout(900); Scrollen wie beim Stillstand. */ letzte: (() => { const e = document.querySelector(".transport"); - const r = document.querySelector(".regiekachel.kachel--sendung"); + const r = document.querySelector(".regiekachel.regiekachel--sendung"); if (!e || !r) return false; /* 4 = DOCUMENT_POSITION_FOLLOWING */ return !!(r.compareDocumentPosition(e) & 4); @@ -850,10 +854,47 @@ await host.seite.waitForTimeout(900); const raus = []; const leiste = document.querySelector(".transport"); if (!leiste) return raus; + /* ==== WAS WEGGEROLLT IST, IST NICHT VERDECKT (08.10.2026) + + Die Regie-Schublade (`#pult-innen`) rollt: Ihr Inhalt ist 1228 + Pixel hoch, sichtbar sind 342. Alles darunter ist + ABGESCHNITTEN -- man rollt es herunter, und dann ist es da. + `getBoundingClientRect()` meldet fuer solche Felder trotzdem + eine Lage im Fenster, und `elementFromPoint` findet dort die + Transportleiste. Die Messung meldete daraufhin „die Leiste + liegt ueber drei Bedienelementen" -- bei Feldern, die man nur + noch nicht heruntergerollt hat. + + GEFUNDEN DURCH EINEN GEGENLAUF, nicht durch Nachdenken: Am + 08.10. kam die Warnung neu, der Umbau sah wie die Ursache aus. + Ein `git worktree` auf den letzten Commit zeigte dieselbe Lage + -- dort lagen die Felder nur zufaellig ein paar Pixel tiefer, + unter dem Fensterrand, und `elementFromPoint` gab `null` + zurueck. Es war also nie eine Frage der Gestaltung, sondern + eine Luecke in der Messung. + + Gefragt wird deshalb zuerst: Liegt das Feld ueberhaupt im + sichtbaren Teil seines Rollkastens? Nur dann kann es verdeckt + sein. */ + const rollkasten = (e) => { + for (let p = e.parentElement; p; p = p.parentElement) { + const c = getComputedStyle(p); + if (/(auto|scroll)/.test(c.overflowY) || /(auto|scroll)/.test(c.overflowX)) return p; + } + return null; + }; for (const e of document.querySelectorAll("#pult-innen button, #pult-innen input," + " #pult-innen select, #pult-innen label")) { const r = e.getBoundingClientRect(); if (r.width === 0 || r.height === 0) continue; + const k = rollkasten(e); + if (k) { + const kr = k.getBoundingClientRect(); + const mitte = r.top + r.height / 2; + /* Ganz oder halb aus dem Rollkasten heraus: weggerollt, + nicht verdeckt. */ + if (mitte < kr.top || mitte > kr.bottom) continue; + } const oben = document.elementFromPoint(r.left + r.width / 2, r.top + r.height / 2); if (!oben || e === oben || e.contains(oben)) continue; if (!leiste.contains(oben)) continue; /* nur die Leiste zaehlt */ @@ -945,6 +986,56 @@ await host.seite.waitForTimeout(900); console.log(" ACHTUNG: Der Knopf zum Teilen fehlt oder ist zu klein."); ausgang = 1; } + + /* ==== NICHTS ÜBER DEM BILD DECKT EINEN KNOPF ZU (08.10.2026) ==== + + Über der Leinwand schweben drei Dinge: die Senderleiste (Kamera, + Bildschirm, Mikro, Geräte), das Band „Ton einschalten" und die + Spendenkarte. Sie liegen alle `absolute` und wissen nichts + voneinander. + + GEFUNDEN AUF EINEM BILDSCHIRMFOTO, NICHT IN EINER ZAHL: Das Band + stand auf `left: 50%, bottom: 20px`, die Leiste auf `left: 16px, + bottom: 16px` — also auf derselben Höhe. Bei vier Knöpfen reichte + die Leiste bis zur Mitte, und vom Mikro-Knopf war nur noch ein + „t" zu sehen. Es war schon vorher so; mit dem fünften Knopf + wurde es nur sichtbar. + + Gemessen wird nicht die Lage, sondern die ÜBERLAPPUNG — dieselbe + Form wie bei der Transportleiste. Eine Zahl („das Band muss 78 + Pixel hoch liegen") wäre wieder die Schwelle, die beim nächsten + Knopf niemand nachzieht. */ + const deckt = await host.seite.evaluate(() => { + const stuecke = [ + ...document.querySelectorAll(".senderleiste button"), + ...document.querySelectorAll(".ton-an"), + ].map((e) => ({ e, r: e.getBoundingClientRect() })) + .filter((x) => x.r.width > 0 && x.r.height > 0); + const raus = []; + for (let i = 0; i < stuecke.length; i++) { + for (let j = i + 1; j < stuecke.length; j++) { + const a = stuecke[i].r, b = stuecke[j].r; + const quer = Math.min(a.right, b.right) - Math.max(a.left, b.left); + const hoch = Math.min(a.bottom, b.bottom) - Math.max(a.top, b.top); + if (quer > 1 && hoch > 1) { + const n = (x) => x.e.id || x.e.className || x.e.tagName; + raus.push(`${n(stuecke[i])} / ${n(stuecke[j])}`); + } + } + } + return { zahl: stuecke.length, raus }; + }); + console.log(` Über dem Bild: ${deckt.zahl} Bedienteile, ` + + `${deckt.raus.length} Überlappung(en)`); + /* DIE ZAHL GEHOERT IN DIE BEDINGUNG. „null Ueberlappungen" ist auch + dann wahr, wenn gar nichts gemessen wurde -- und genau so sieht + ein gruener Haken aus, der nichts beweist. */ + if (deckt.zahl < 4 || deckt.raus.length) { + console.log(` ACHTUNG: ${deckt.raus.length ? deckt.raus.join(" · ") + : "zu wenige Bedienteile gefunden -- die Messung greift ins Leere"}`); + ausgang = 1; + } + await bild(host.seite, "23-regieplatz"); } @@ -1140,7 +1231,16 @@ await zuschauer.seite.evaluate(() => { Versehen. */ muenze: (n.querySelector(".spendenkarte__bild img") ?.getAttribute("src") || "").split("/").pop(), - kleinWeg: n.dataset?.muenze === "ja", + /* ==== DIE MARKIERUNG SITZT AN DER KARTE, NICHT AN DER + HUELLE (08.10.2026) + + Angehaengt wird seit heute die HUELLE -- sie macht das + Erscheinen, die Karte darin die Form. `n.dataset.muenze` + fragte also den falschen Knoten und war immer leer: + „Die Muenze steht zweimal auf derselben Karte", bei + einer Karte, auf der sie genau einmal stand. */ + kleinWeg: (n.matches?.(".spendenkarte") + ? n : n.querySelector?.(".spendenkarte"))?.dataset?.muenze === "ja", }); } } @@ -1153,7 +1253,7 @@ await roh("/workspace/api/spenden", { rumpf: { dogen_mille: 2500, name: "Nachtfalter", gruss: "Fuer das Rudel!" }, }); await zuschauer.seite.waitForFunction( - () => document.querySelector('.spendenkarte[data-da="ja"]'), null, { timeout: 8000 }, + () => document.querySelector('.spendenkarte-auftritt[data-da="ja"] .spendenkarte'), null, { timeout: 8000 }, ).catch(() => {}); await zuschauer.seite.waitForTimeout(700); await bild(zuschauer.seite, "10-spendenkarte"); @@ -1477,13 +1577,13 @@ if (!wartet) { naechste kommt erst, wenn die vorige abgelaufen ist. Also auf das Merkmal warten, das nur die neue hat. */ await host.seite.waitForFunction( - () => [...document.querySelectorAll('.spendenkarte[data-da="ja"]')].some( + () => [...document.querySelectorAll('.spendenkarte-auftritt[data-da="ja"] .spendenkarte')].some( (k2) => k2.querySelector(".spendenkarte__wer")?.textContent === "Probe" && k2.querySelector(".spendenkarte__bild img")), null, { timeout: 20000 }, ).catch(() => {}); const nachWechsel = await host.seite.evaluate(() => { - const k2 = [...document.querySelectorAll('.spendenkarte[data-da="ja"]')] + const k2 = [...document.querySelectorAll('.spendenkarte-auftritt[data-da="ja"] .spendenkarte')] .find((x) => x.querySelector(".spendenkarte__wer")?.textContent === "Probe"); const b = k2?.querySelector(".spendenkarte__bild img"); /* Und auch hier: geladen, nicht nur vorhanden. */ @@ -1966,6 +2066,12 @@ await host.seite.waitForTimeout(1200); const tafelnLeeren = async () => { for (const s2 of [host.seite, zuschauer.seite]) { await s2.evaluate(() => { + /* Die HUELLE wegnehmen und nicht nur die Karte darin: Seit dem + 08.10.2026 macht die Huelle das Erscheinen, die Karte nur die + Form. Wer nur die Karte entfernt, laesst eine leere Huelle + stehen -- und misst gleich darauf, ob „schon eine Karte da + ist", mit einem Rest der vorigen im Bild. */ + for (const k2 of document.querySelectorAll(".spendenkarte-auftritt")) k2.remove(); for (const k2 of document.querySelectorAll(".spendenkarte")) k2.remove(); }); } @@ -1993,10 +2099,27 @@ const messeKarte = async (erwartet) => { Sonst koennte die vorige Probe die Antwort geben, und die Messung bestaetigte eine Aenderung, die gar nicht angekommen ist. */ + /* ==== GEWARTET WIRD AUF DEN FAKTOR, NICHT AUF EINE PIXELZAHL + (geaendert 08.10.2026) + + Hier stand „warte, bis die Schrift 16 bzw. 32 Pixel misst". Das + war die Grundgroesse der Karte AM RAND. Seit heute steht sie in + der Mitte und ist dort groesser (1,3rem statt 1rem) -- und damit + wartete die Messung auf eine Zahl, die es nicht mehr gibt, lief + in die Frist und meldete „Die Probe zeigt keine Karte". Die + Karte war die ganze Zeit da. + + DAS IST DIESELBE FALLE WIE EINE FESTE SCHWELLE IM STILBLATT: + eine Zahl, die irgendwo anders entsteht, hier abgeschrieben. + `--sp-gross` ist der Wert, um den es geht -- er kommt von der + Stufe, und die Messung fragt jetzt genau ihn ab. Wie viele + Pixel daraus werden, entscheidet die Gestaltung und darf sich + aendern. */ await host.seite.waitForFunction( - (soll) => [...document.querySelectorAll('.spendenkarte[data-da="ja"]')].some((k2) => - k2.querySelector(".spendenkarte__wer")?.textContent === "Probe" - && Math.abs(parseFloat(getComputedStyle(k2).fontSize) - soll) < 0.5), + (soll) => [...document.querySelectorAll('.spendenkarte-auftritt[data-da="ja"] .spendenkarte')] + .some((k2) => k2.querySelector(".spendenkarte__wer")?.textContent === "Probe" + && Math.abs(parseFloat( + getComputedStyle(k2).getPropertyValue("--sp-gross")) - soll) < 0.01), erwartet, { timeout: 16000 }, ).catch(() => {}); /* DIE KARTE KOMMT VON LINKS HEREIN (450 ms). Wer sofort misst, @@ -2012,7 +2135,7 @@ const messeKarte = async (erwartet) => { -- gemessen wurden dadurch 413 statt 430 Pixel (genau das 0,96-fache) und ein linker Rand von -1. Beides war kein Fehler der Karte, sondern ein Blick zur falschen Zeit. */ - const k2 = [...document.querySelectorAll('.spendenkarte[data-da="ja"]')] + const k2 = [...document.querySelectorAll('.spendenkarte-auftritt[data-da="ja"] .spendenkarte')] .find((x) => x.querySelector(".spendenkarte__wer")?.textContent === "Probe"); if (!k2) return null; const r = k2.getBoundingClientRect(); @@ -2036,7 +2159,7 @@ const messeKarte = async (erwartet) => { }); }; -const kKlein = await messeKarte(16); +const kKlein = await messeKarte(1); console.log(` Größe 1: Karte ${kKlein?.breit}x${kKlein?.hoch} px, Schrift ${kKlein?.schrift} px, ` + `Betrag „${kKlein?.betragText}" bei ${kKlein?.betragSchrift} px, Stufe „${kKlein?.stufe}"`); if (!kKlein || kKlein.wer !== "Probe") { @@ -2065,7 +2188,7 @@ await host.seite.evaluate(() => { document.getElementById("stufen-sichern").click(); }); await host.seite.waitForTimeout(1400); -const kGross = await messeKarte(32); +const kGross = await messeKarte(2); console.log(` Größe 2: Karte ${kGross?.breit}x${kGross?.hoch} px, Schrift ${kGross?.schrift} px, ` + `Betrag bei ${kGross?.betragSchrift} px, links ${kGross?.links} / oben ${kGross?.oben}, ` + `ganz im Bild=${kGross?.imBild}`); @@ -2100,7 +2223,7 @@ if (!kGross || !kKlein) { /* ---- Und die Farbe? ---- */ const farbe = await host.seite.evaluate(() => { - const k2 = [...document.querySelectorAll('.spendenkarte[data-da="ja"]')] + const k2 = [...document.querySelectorAll('.spendenkarte-auftritt[data-da="ja"] .spendenkarte')] .find((x) => x.querySelector(".spendenkarte__wer")?.textContent === "Probe"); return k2 ? getComputedStyle(k2).getPropertyValue("--sp-farbe").trim() : ""; }); @@ -2161,6 +2284,34 @@ if (tempoKnoepfe.length !== 6 || tempoKnoepfe.some((t) => t.breit < 44)) { ausgang = 1; } +/* ==== ERST AN DEN ANFANG, DANN MESSEN (08.10.2026) ================ + + Diese Messung braucht zwoelf Sekunden Videomaterial -- sechs bei + einfachem und sechs bei doppeltem Tempo, und bei doppeltem sind + das zwoelf Sekunden Film. Das Testvideo ist 3:34 lang, und was + davon noch uebrig ist, haengt davon ab, wie lange alle Abschnitte + davor gebraucht haben. + + GENAU DAS HAT SIE FLATTERN LASSEN: Am 08.10. meldete sie in zwei + von vier Laeufen „Die Geschwindigkeit wirkt nicht" -- einmal mit + „2x: 0.00", einmal mit „2x: 0.50". Beides heisst nicht „zu + langsam", sondern „das Video war zu Ende". Im Gegenlauf auf dem + letzten Commit war es gruen, und auch das war Zufall: Dort lag der + Spieler zufaellig weit genug vorn. + + Eine Pruefung, deren Ergebnis davon abhaengt, wie schnell der + Rechner die Abschnitte davor abgearbeitet hat, misst den Rechner. + Also: vorher an den Anfang springen. Dann sind immer 3:34 + uebrig. */ +await host.seite.evaluate(() => { + const band = document.getElementById("v-band"); + if (!band) return; + band.value = "20"; /* 2 % von 3:34 ~ 4 s */ + band.dispatchEvent(new Event("input", { bubbles: true })); + band.dispatchEvent(new Event("change", { bubbles: true })); +}); +await host.seite.waitForTimeout(1500); + /* NORMALES TEMPO ZUERST MESSEN -- als Bezugswert. Ohne ihn wüsste man nicht, ob 1,5x wirklich schneller ist oder ob das Video einfach schon lief. */ @@ -2188,7 +2339,15 @@ const normal = (a1 !== null && a2 !== null) ? (a2 - a1) / 6 : null; const schnell = (b1 !== null && b2 !== null) ? (b2 - b1) / 6 : null; console.log(` 1x: ${normal === null ? "?" : normal.toFixed(2)} Sekunden je Sekunde · ` + `2x: ${schnell === null ? "?" : schnell.toFixed(2)}`); -if (normal === null || schnell === null || schnell < normal * 1.5) { +/* UND DIE BEDINGUNG NENNT, WAS SIE GEMESSEN HAT. „schnell < normal * + 1,5" ist auch dann wahr, wenn beide null sind -- also wenn der + Spieler ueberhaupt nicht lief. Das ist ein anderer Befund als + „das Tempo wirkt nicht", und er braucht einen anderen Satz. */ +if (normal === null || schnell === null || normal < 0.5) { + console.log(" KONNTE NICHT NACHSEHEN: Das Video lief waehrend der Messung nicht " + + `(1x: ${normal === null ? "?" : normal.toFixed(2)}). Das sagt nichts ueber das Tempo.`); + offenePunkte.push("Das Tempo war nicht messbar -- das Video lief nicht."); +} else if (schnell < normal * 1.5) { console.log(" ACHTUNG: Die Geschwindigkeit wirkt nicht -- das Video laeuft gleich schnell."); ausgang = 1; } @@ -2373,10 +2532,32 @@ if (nachHin.titel === nachZurueck.titel) { console.log(" ACHTUNG: Der Umschalter wechselt nicht."); ausgang = 1; } -/* Die gemerkte Stelle: etwa dort weiter, wo es aufgehoert hat -- - sechs Sekunden Spiel fuer das Laden des Videos. */ +/* ==== DIE GEMERKTE STELLE -- UND WARUM NUR NACH UNTEN GEPRUEFT WIRD + (praezisiert 08.10.2026) + + Hier stand `Math.abs(nachher - vorher) > 8`. Das misst zwei Dinge + auf einmal: ob die Stelle gemerkt wurde UND wie lange diese + Messung zwischendurch gewartet hat. Sie wartet 2x3 Sekunden, und + danach laeuft das zurueckgeholte Video ganz richtig WEITER -- + gemessen wurden deshalb 86 -> 95, und die Pruefung nannte das + „verloren". + + Im Gegenlauf auf dem letzten Commit stand dieselbe Zahl bei + 213 -> 213, und das sah nach „alles in Ordnung" aus. Es war + Zufall: Das Video stand dort bei 213 von 214 Sekunden, also am + Ende, und konnte gar nicht weiterlaufen. Eine Pruefung, die nur + am Ende eines Videos gruen ist, prueft den Spieler und nicht die + Software. + + WAS WIRKLICH SCHIEFGEHEN KANN, geht nach UNTEN: Die Stelle wird + vergessen (zurueck auf 0) oder es wird die Stelle des ANDEREN + Videos genommen (hier wenige Sekunden). Beides liegt deutlich + unter dem gemerkten Wert. Nach oben ist Weiterlaufen der + Normalfall; die Obergrenze bleibt nur als Netz gegen einen Sprung + ins Nichts. */ if (stelleVorher === null || stelleNachher === null - || Math.abs(stelleNachher - stelleVorher) > 8) { + || stelleNachher < stelleVorher - 2 + || stelleNachher > stelleVorher + 30) { console.log(` ACHTUNG: Die gemerkte Stelle ging verloren ` + `(${stelleVorher} -> ${stelleNachher}).`); ausgang = 1; @@ -2760,6 +2941,151 @@ await roh(`/workspace/api/reaktion/massnahme/${IDS.gast}`, { method: "DELETE", k } } +/* ===================================================================== + DER VORRAUM FUER EINGELADENE GAESTE (08.10.2026) + + Filipe: „wenn gäste eingeladen werden, sollen die akzeptieren oder + ablehnen können. bevor die auch angezeigt werden sollen die + auswählen können ob die kamera an ist oder aus ist." + + --------------------------------------------------------------------- + WARUM DAS HIER GEMESSEN WIRD UND NICHT NUR IN `pruef-reaktion` + + Dort ist der ganze Weg geprueft -- fragen, zusagen, ablehnen, + Frist, Kameravorwahl. Was dort NICHT geprueft werden kann, ist, ob + der Vorhang auf dem Bildschirm auch wirklich erscheint. Ein + Zustand im Server, den die Seite nicht zeigt, ist fuer den + Gefragten dasselbe wie keine Einladung: Er sieht nichts, sagt + nichts, und der Host wartet zwei Minuten. + + Deshalb wird hier gemessen, was ein Mensch sieht: Steht der + Vorhang da? Hat er BEIDE Ausgaenge? Laeuft das eigene Bild darin? + Und bringt „Dabei sein" ihn wirklich auf die Buehne? + ===================================================================== */ +console.log(""); +console.log("=== Der Vorraum fuer eingeladene Gaeste ==="); +{ + /* Der Zuschauer war eben aus der Sendung genommen und + zurueckgeholt -- sein Vorhang steht noch (siehe der Abschnitt + darueber). Also neu laden, so wie es ein Mensch auch taete. */ + await zuschauer.seite.reload({ waitUntil: "networkidle" }); + await zuschauer.seite.waitForTimeout(900); + + /* PLATZ SCHAFFEN. Die Buehne hat zwei Plaetze, und aus frueheren + Abschnitten steht dort noch jemand. Eine Einladung, die mit + „gaeste_voll" abgelehnt wird, maesse nichts ueber den Vorraum. */ + const vorher = await roh("/workspace/api/reaktion", { keks: k.admin }); + for (const g of (vorher.j?.gaeste || [])) { + await roh(`/workspace/api/reaktion/gast/${g.id}`, { method: "DELETE", keks: k.admin }); + } + + const ein = await roh("/workspace/api/reaktion/gast", { + method: "POST", keks: k.admin, rumpf: { person_id: IDS.gast }, + }); + console.log(` Eingeladen: ${ein.code} (${(ein.j?.einladungen || []).length} offene Frage)`); + + await zuschauer.seite.waitForFunction( + () => { const v = document.getElementById("vorraum"); return !!v && !v.hidden; }, + null, { timeout: 15000 }, + ).catch(() => {}); + await zuschauer.seite.waitForTimeout(600); + + const vr = await zuschauer.seite.evaluate(() => { + const v = document.getElementById("vorraum"); + if (!v || v.hidden) return { da: false }; + const sicht = (e) => { + if (!e) return null; + const r = e.getBoundingClientRect(); + return { + b: Math.round(r.width), h: Math.round(r.height), + imBild: r.width > 0 && r.height > 0 && r.top >= -1 && r.left >= -1 + && r.bottom <= window.innerHeight + 1 && r.right <= window.innerWidth + 1, + }; + }; + const vid = document.getElementById("vorraum-video"); + return { + da: true, + /* DECKT ER WIRKLICH ALLES ZU? Ein Vorhang, durch den man den + Saal weiterbedienen kann, ist keiner -- und ausgerechnet + hier soll man nicht nebenbei etwas anderes anklicken. */ + ueberAllem: (() => { + const e = document.elementFromPoint(window.innerWidth / 2, 12); + return !!e && (e === v || v.contains(e)); + })(), + ja: sicht(document.getElementById("vorraum-ja")), + nein: sicht(document.getElementById("vorraum-nein")), + kamera: sicht(document.getElementById("vorraum-kamera")), + mikro: sicht(document.getElementById("vorraum-mikro")), + /* LAEUFT DAS EIGENE BILD? `videoWidth` ist die einzige ehrliche + Antwort -- ein `