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 `