Die Chatkiste steht immer, Beenden raeumt auf -- und eine verspaetete Antwort ueberschreibt nichts Neueres mehr

Filipe, 30.09.2026: „wenn eine sendung beendet wurde soll alles
automatisch verschwinden. also von daten die da stehen von dem video."
Und: „mach dass ich die chat kiste immer sehe bitte und nicht erst
wenn das video läuft."

=====================================================================
1. BEENDET HEISST LEER
=====================================================================
Beim Beenden verschwinden Titel, YouTube-Adresse, Videotitel,
Vorschaubild, Beginn und das zweite Video samt seiner Stelle. Vorher
blieb alles stehen; beim naechsten Aufmachen stand dort der Titel der
letzten Sendung und ein Beginn, der in der Vergangenheit liegt.

ERST DER VERLAUF, DANN DAS LEEREN -- weg vom Schreibtisch heisst
nicht weg aus der Welt. Geprueft: Nach dem Beenden steht die Sendung
weiter in `reaktion_verlauf`, samt Video.

EINE FELDLISTE, NICHT ZWEI. Die Aufzaehlung stand in der
Zuruecksetzen-Route; jetzt brauchen sie zwei Wege. `SENDUNGS_FELDER`
und `PULT_EINSTELLUNGEN` stehen einmal oben. Zwei Abschriften waeren
die, die beim naechsten Spaltenzuwachs auseinanderlaufen -- genau so
sind am 11.09. drei Spalten mit Inhalt verlorengegangen.

Einstellungen (Bildaufteilung, Kameragroesse, Chatmodus) und die
Warteschlange bleiben -- die raeumt weiter nur „Alles zuruecksetzen"
ab. Wer nur vorbereitet und dann zumacht, behaelt seine Eingaben; das
ist eine Entscheidung und steht als Pruefung fest.

DAS FELD „BEGINN" WURDE NIE GELEERT, nur gefuellt:
`if (lage.beginnt_am && document.activeElement !== …)` -- zwei Fragen
in einer Bedingung, und die zweite beantwortete die erste
stillschweigend mit nein. Das betraf auch den vorhandenen
Zuruecksetzen-Knopf.

=====================================================================
2. DIE CHATKISTE STEHT IMMER
=====================================================================
Sie lag in `#teil-live` und ging mit ihm weg. Im Wartebereich stand
dabei woertlich „Der Chat ist schon offen" -- der Satz stimmte sogar,
der Server laesst dort schreiben (201, geprueft); zu sehen war der
Chat trotzdem nicht.

Die drei Haeute und die Schiene liegen jetzt in einem gemeinsamen
`.raum`: links wechseln die Haeute, rechts steht der Chat. Das Raster
wandert mit nach oben statt sich zu verdoppeln -- zwei
`grid-template-columns` fuer dieselbe Frage waren am 28.09. der
Grund, warum die Regieleiste null Pixel bekam.

Wer nicht schreiben darf, liest den Grund im Feld („Der Saal ist zu").

DER UMBAU HAT DREI DINGE VERSCHOBEN, und die Messung hat sie sofort
gefunden: Kino 0 px hoch, Spendenkarte bei x=1458 ausserhalb des
Bildes, „Kamera aus"-Schild kam nicht an. Eine Ursache: Zwei Regeln
(`grid-row: 2` und `grid-area: kino`) zielten auf die drei Haeute,
weil die frueher unmittelbar im Saal lagen. Gefunden hat das nicht
das Nachdenken, sondern eine Messung der ganzen Kette --
`teil-live 0x0 @1440,742` lag NEBEN dem Raum.

=====================================================================
3. „DER SAAL IST ZU" ERREICHTE NIEMANDEN, DER DARIN SASS
=====================================================================
`stand: "zu"` loescht `reaktion_dabei`, und `melden()` suchte seine
Empfaenger erst danach -- in genau dieser geleerten Liste. Die Seiten
der Zuschauer blieben auf „live" stehen.

`melden(art, daten, wer)` nimmt jetzt eine Empfaengerliste; die
Stand-Route bestimmt sie VOR jeder Aenderung.

UND EINE ZUGESPERRTE SEITE VERSTUMMTE FUER IMMER. War der Saal zu,
hielt `anschliessen()` jeden Takt an -- und Rundrufe bekam sie auch
keine, weil sie in keiner Liste mehr stand. Machte der Host wieder
auf, passierte bei allen mit offenem Reiter NICHTS. Nicht in dreissig
Sekunden: nie, bis jemand von Hand neu lud. Gemessen: Server sagt
„vorbereitung", die Seite zeigt „zu", auch nach 40 Sekunden.
Jetzt fragt eine geschlossene Seite alle 15 Sekunden nach -- der
ruhigste Zustand des Hauses, dort kostet das nichts.

=====================================================================
4. WER ZULETZT ANTWORTET, HAT NICHT RECHT
=====================================================================
Eine Abfrage ist Sekundenbruchteile unterwegs. Kommt in dieser Zeit
ein Rundruf an, ist SEIN Stand der neuere -- und trotzdem hat die
verspaetete Antwort ihn ueberschrieben.

Gemessen: Die Regie schaltet die Werbung auf Wechsel, der Rundruf
zeichnet ihn, und einen Augenblick spaeter steht wieder Laufband.
Eine feste Wartezeit in der Messung hatte das zugedeckt; es fiel
erst auf, als ich sie durch eine echte Bedingung ersetzte.

Drei Runden Raten lagen daneben. Gefunden hat es eine Mitschrift der
Aufrufkette:

    wechsel:2  @ EventSource
    laufband:2 @ zeichnen < standHolen < signalEmpfangen

Eine Folgenummer zaehlt jeden angekommenen Rundruf. Jede Abfrage, die
die Seite VON SICH AUS macht -- nach dem Verbindungsaufbau, im
geschlossenen Saal, bei jedem Verbindungssignal, nach einer
abgelehnten Anwesenheitsmeldung, und der Anwesenheitstakt selbst --
verwirft ihr Ergebnis, wenn inzwischen etwas Neueres da war.

Abfragen nach einer EIGENEN Handlung bleiben hart: Sie bringen Dinge
mit, die kein Rundruf traegt (die Verwaltungslisten der Regie). Die
Unterscheidung steht am Aufruf, nicht in einer Bedingung im Inneren.

=====================================================================
5. UND DIE MESSUNGEN WARTEN NICHT MEHR AUF DIE UHR
=====================================================================
Feste Wartezeiten sind dieselbe Falle wie feste Schwellen: Sie
stimmen, bis daneben etwas langsamer wird, und melden dann einen
Fehler, den es nicht gibt. Zweimal ist mir das in einer Stunde
passiert -- einmal beim Band, einmal bei den Ecken. Der Werbeblock
wartet jetzt fuenfmal auf das Ergebnis, der Gleichlauf viermal; laeuft
eine Frist ab, ist es ein echter Befund.

Die erste Fassung der Gleichlauf-Gegenprobe war selbst falsch -- sie
hielt das Selbstheilen des Systems fuer einen Fehler.

Neu in mess-reaktion: „Die Chatkiste in jedem Zustand" (alle drei,
mit Breite und Sperrgrund) und „Beendet heisst leer (im Pult)" -- dort
gemessen, wo Filipe hinsieht, nicht in der Datenbank.
Neu in pruef-reaktion (+14): das Leeren, die Gegenprobe „vorher war
etwas drin", der Verlauf bleibt, und die Entscheidung „wer nur
vorbereitet hat, behaelt seine Eingaben".

Gemessen: mess-reaktion dreimal hintereinander 0/0, pruef-reaktion
407/0, pruef-css-klassen 37/0, pruef-struktur 44/0, pruef-tippziele
13/0, pruef-haus-seiten 38/0, pruef-haus-trennung 100/0, pruef-buehne
38/0, pruef-push-ziel 38/0, mess-buehne 0.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-30 02:36:13 +02:00
co-authored by Claude Opus 5
parent 2c3b90d75c
commit 0fbd3f45b3
50 changed files with 1644 additions and 933 deletions
+84 -19
View File
@@ -98,6 +98,41 @@ export const REAKTION_HOST_ROLLEN = HOST_ROLLEN;
const jetzt = () => new Date().toISOString();
/* ======================================================================
WAS ZU EINER EINZELNEN SENDUNG GEHOERT (30.09.2026)
Filipe: „wenn eine sendung beendet wurde soll alles automatisch
verschwinden. also von daten die da stehen von dem video."
Diese Aufzaehlung stand bis dahin EINMAL mitten in der
Zuruecksetzen-Route. Jetzt brauchen sie zwei Wege -- das Beenden
und der Knopf --, und zwei Abschriften waeren die, die beim
naechsten Spaltenzuwachs auseinanderlaufen. Genau so sind am
11.09. drei Spalten mit Inhalt verlorengegangen.
SIE STEHT EINZELN DA UND NICHT ALS „alles ausser". Eine Liste von
Ausnahmen waechst still mit jeder neuen Spalte mit -- und dann
loescht das Aufraeumen etwas, das es nie loeschen sollte. Was hier
fehlt, bleibt stehen, und das ist die sichere Richtung.
====================================================================== */
const SENDUNGS_FELDER = {
titel: "", video: "", video_titel: "", vorschau: "",
beginnt_am: null, laeuft: 0, sekunde: 0, gestartet_am: null,
zweit_video: "", zweit_titel: "", zweit_sekunde: 0,
};
/* UND WAS EINSTELLUNG IST -- nur beim ausdruecklichen Zuruecksetzen.
Bildaufteilung, Kameragroesse und Chatmodus gelten fuer alle
Sendungen. Wer sie nach jedem Beenden neu setzen muesste, haette
nach der dritten Sendung genug davon -- und schaltet dann lieber
gar nicht mehr ab. Das ist der Unterschied zwischen Aufraeumen und
Abraeumen. */
const PULT_EINSTELLUNGEN = {
tempo: 1, layout: "kino", kamera_gross: 1, kamera_ecke: "ur",
chat_modus: "offen",
};
/* ---------- Schranken --------------------------------------------------- */
function angemeldet(req, res, next) {
@@ -484,8 +519,15 @@ function darfSchreiben(person, stand) {
return schreibGrund(person, stand) === null;
}
function melden(art, daten) {
stromAn(empfaenger(), art, daten);
/** Rundruf an alle, die von einer Aenderung erfahren sollen.
*
* `wer` ist ausdruecklich ueberschreibbar (30.09.2026): Eine
* Aenderung, die die Zuschauerliste LEERT, muss ihre Empfaenger
* vorher bestimmt haben. Sonst sucht der Rundruf sie in einer
* Liste, die es nicht mehr gibt -- und ausgerechnet die Nachricht
* „der Saal ist zu" erreicht niemanden, der darin sitzt. */
function melden(art, daten, wer) {
stromAn(wer || empfaenger(), art, daten);
/* UND AN DIE BUEHNENQUELLEN. Sie haengen an keiner Person -- OBS
meldet sich mit einem Schluessel, nicht mit einem Zugang. Beide
Aufrufe stehen hier, an EINER Stelle: zwei Stellen, die je einen
@@ -539,19 +581,16 @@ reaktionRouter.post("/workspace/api/reaktion/zuruecksetzen", nurHost, (req, res)
db().exec("BEGIN IMMEDIATE");
try {
/* DIE SPALTEN STEHEN EINZELN DA UND NICHT ALS „alles ausser".
Eine Liste von Ausnahmen waechst still mit jeder neuen
Spalte mit -- und dann loescht das Zuruecksetzen etwas, das
es nie loeschen sollte. Was hier fehlt, bleibt stehen, und
das ist die sichere Richtung. */
db().prepare(`UPDATE reaktion SET
stand = 'zu', titel = '', video = '', video_titel = '', vorschau = '',
beginnt_am = NULL, laeuft = 0, sekunde = 0, tempo = 1,
layout = 'kino', kamera_gross = 1, kamera_ecke = 'ur',
chat_modus = 'offen',
zweit_video = '', zweit_titel = '', zweit_sekunde = 0,
gestartet_am = NULL, stand_seit = ?, geaendert = ?
WHERE id = 1`).run(jetzt(), jetzt());
/* BEIDE LISTEN, und beide stehen oben bei den Tabellen -- der
Knopf raeumt mehr weg als das blosse Beenden: auch die
Einstellungen und die Warteschlange. */
const felder = {
...SENDUNGS_FELDER, ...PULT_EINSTELLUNGEN,
stand: "zu", stand_seit: jetzt(), geaendert: jetzt(),
};
const spalten = Object.keys(felder).map((k) => `${k} = ?`).join(", ");
db().prepare(`UPDATE reaktion SET ${spalten} WHERE id = 1`)
.run(...Object.values(felder));
db().prepare("DELETE FROM reaktion_liste").run();
db().prepare("DELETE FROM reaktion_gaeste").run();
db().exec("COMMIT");
@@ -635,6 +674,13 @@ reaktionRouter.post("/workspace/api/reaktion/stand", nurHost, express.json({ lim
if (!STAENDE.includes(neu)) return res.status(400).json({ fehler: "stand_unbekannt" });
const s = sendung();
/* WER DAVON ERFAHREN SOLL, STEHT JETZT FEST -- vor jeder
Aenderung. „zu" loescht `reaktion_dabei`; wuerde der Rundruf
seine Empfaenger erst danach suchen, ginge ausgerechnet die
Nachricht „der Saal ist zu" an niemanden, der darin sitzt.
Ihre Seite blieb dann auf „live" stehen, bis sie neu luden. */
const erfahren = empfaenger();
/* OHNE VIDEO KEIN LIVE. Der Knopf waere sonst da, das Ergebnis
ein schwarzes Rechteck -- und die Leute sitzen davor. */
if (neu === "live" && !s.video) {
@@ -664,9 +710,28 @@ reaktionRouter.post("/workspace/api/reaktion/stand", nurHost, express.json({ lim
dabeiListe().length, zahl?.n || 0);
db().exec("DELETE FROM reaktion_chat");
db().exec("DELETE FROM reaktion_gaeste");
felder.laeuft = 0;
felder.sekunde = 0;
felder.gestartet_am = null;
/* UND DER SCHREIBTISCH WIRD LEER (30.09.2026).
Filipe: „wenn eine sendung beendet wurde soll alles
automatisch verschwinden. also von daten die da stehen von
dem video."
Vorher blieben Titel, Video, Vorschau, Beginn und das
zweite Video stehen. Beim naechsten Aufmachen stand dort
der Titel der letzten Sendung und ein Beginn, der in der
Vergangenheit liegt -- man musste jedes Feld einzeln
leeren, bevor man anfangen konnte.
ERST DER VERLAUF, DANN DAS LEEREN. Die Zeile darueber
schreibt Titel und Video in `reaktion_verlauf`; umgekehrt
stuende dort nichts. Verloren geht also nichts -- es wandert
nur dorthin, wo Vergangenes hingehoert.
Die Einstellungen (Bildaufteilung, Kameragroesse,
Chatmodus) und die Warteschlange bleiben. Wer sie auch weg
haben will, hat dafuer den Knopf „Alles zuruecksetzen". */
Object.assign(felder, SENDUNGS_FELDER);
}
if (neu === "zu") db().exec("DELETE FROM reaktion_dabei");
@@ -680,7 +745,7 @@ reaktionRouter.post("/workspace/api/reaktion/stand", nurHost, express.json({ lim
ip: echteIp(req),
});
const l = lage(req.person);
melden("reaktion", { was: "stand", lage: oeffentlich(l) });
melden("reaktion", { was: "stand", lage: oeffentlich(l) }, erfahren);
/* ERST JETZT EINLADEN, nach dem Schreiben und nach `melden`.
Wer die Meldung antippt, landet auf einer Seite, die den