Files
dogfather-universe/workspace/reaktion-foyer.html
T
DogFatherGitandClaude Opus 5 ab204e96f9 Der Sendeplan: ein Kalender in der Regie -- und getippter Text bleibt stehen
Filipe: „in der regie will ich auch video hinzufügen können für andere
tage und uhrzeiten, gerade geht das nicht. wie so ein kalender wo vanvan
und dogfather videos eintragen und vorbereiten können. und wenn ich
texte eingebe oder videos und kurz was anderes mache und es nicht
gespeichert hab löscht es sich von selbst. es soll bleiben bist ich
fertig bin und speichern drücke. oder aus der seite gehe aber nicht
solange wie ich noch da bin."

Haus: Team Dogi (crew.dogfather-universe.com). Die Agentur ist nicht
berührt -- pruef-haus-trennung 107/0.

======================================================================
1. GETIPPTER TEXT ÜBERLEBT DAS NEUZEICHNEN
======================================================================

WARUM ER VERSCHWUNDEN IST: Die Regie zeichnet sich bei jedem Ereignis
neu -- jemand kommt dazu, der Chat bekommt eine Zeile, das Video springt
eine Sekunde weiter. Dabei werden Listen von Grund auf gebaut
(`kasten.textContent = ''`), und jedes Eingabefeld darin entsteht neu,
gefüllt mit dem Wert vom Server.

Es gab einen Notbehelf: `if (document.activeElement !== feld)`, also
„überschreib es nicht, solange der Finger drinsteht". Filipe beschreibt
den Fall, in dem der zu kurz greift, wörtlich: „und kurz was anderes
mache". Wer tippt und dann woanders hinklickt, verliert den Fokus -- und
beim nächsten Takt auch seinen Text. An einem Sendeabend sind das
hunderte Gelegenheiten.

DIE FRAGE IST NICHT „HAT ES DEN FOKUS", SONDERN „IST ES SAUBER". Steht
im Feld genau das, was der Server zuletzt geliefert hat, darf eine neue
Auskunft es ersetzen -- sie ist aktueller. Weicht es ab, hat ein Mensch
etwas hineingetan, das noch nirgends steht; dann gewinnt der Mensch.

Das hat zwei Nebenwirkungen, und beide sind gewollt:
  * Wer NICHTS angefasst hat, sieht Änderungen des anderen sofort.
  * Wer etwas angefasst hat, verliert es auch dann nicht, wenn dieselbe
    Stelle gerade von jemand anderem geändert wurde.

EIN NEUER BAUSTEIN, NICHT ZWANZIG EINZELFÄLLE: `assets/js/getippt.js`.
Ein Feld wird an seinen Serverwert gebunden, meldet beim Tippen, was
offen ist, und wird nach dem Speichern wieder sauber. Benutzt an zehn
Stellen der Regie: Titel, Video, Vorschaubild, Startzeit, zweites Video,
die Termine der Warteschlange, Name und Gruß an jeder Spende, die
fertigen Sätze.

DER SCHLÜSSEL TRÄGT DIE KENNUNG, NICHT DIE POSITION (`warte-wann-7`,
nicht „Zeile 3"). Sonst landete der getippte Termin in der falschen
Zeile, sobald jemand eine nach oben schiebt.

IM SPEICHER UND NICHT IN `localStorage` -- genau wie Filipe es gesagt
hat: „oder aus der seite gehe". Ein halber Satz, der drei Tage später
wieder auftaucht, ist keine Hilfe; man weiß dann nicht mehr, ob er
gelten soll.

UND MAN SIEHT ES: Ein ungespeichertes Feld bekommt eine bernsteine Kante
links -- dieselbe Farbe wie ein überfälliger Termin. Keine eingefärbte
Fläche: Die schreit, eine Kante sagt dasselbe und lässt den Text in
Ruhe. Ohne diese Marke wäre die Reparatur halb -- der Text stünde da,
sähe aus wie gespeichert, und niemand drückte mehr auf „Sichern".

======================================================================
2. DER KALENDER
======================================================================

Es ging bisher nur in zwei Schritten: anhängen, dann in der Zeile den
Termin setzen. Wer einen Abend für nächste Woche plant, macht das je
Video zweimal -- und der zweite Handgriff ist der, den man vergisst.
Jetzt steht das Terminfeld neben der Adresse, und `POST /liste` nimmt
`wann` entgegen.

ZWEI SICHTEN AUF DIESELBE LISTE, keine zweite Tabelle:
  Reihenfolge  -> „was kommt als Nächstes" (mitten in einer Sendung)
  Kalender     -> „wann läuft was" (beim Vorbereiten)
Eine zweite Tabelle „Sendeplan" wären zwei Antworten auf denselben
Bestand, und spätestens beim ersten Verschieben liefen sie auseinander.

EIN MONATSRASTER UND KEINE WOCHE. Wer einen Abend plant, denkt in
„nächsten Donnerstag", nicht in „in sechs Tagen". Tage mit etwas darin
tragen die ANZAHL und nicht nur einen Punkt -- „da ist etwas" und „da
sind vier" sind zwei verschiedene Auskünfte. Heute trägt einen Ring, was
vorbei ist und noch dasteht, wird warm markiert.

Das × an einem Eintrag nimmt NUR DEN TERMIN weg; das Video bleibt in der
Warteschlange. Gelöscht wird in der Reihenfolge-Sicht, und zwar nur
dort, damit es beim Umplanen nicht aus Versehen passiert.

EINE TERMINPRÜFUNG, ZWEI WEGE: `terminLesen()` im Server -- das
Anhängen und das nachträgliche Ändern fragen dieselbe Funktion. Zwei
Abschriften wären zwei Antworten auf „ist das ein Datum", und die zweite
wäre die, die beim nächsten Umbau stehen bleibt. Leer ist gültig und
bleibt der Normalfall.

======================================================================
3. EIN FUND, DER NEBENBEI HERAUSFIEL
======================================================================

`mess-regie` wollte nur ein Neuzeichnen auslösen -- VanVan hängt ein
Video an, DogFather hat die Regie offen -- und wartete vergeblich. Nach
zehn Sekunden war es bei ihm immer noch nicht da.

`empfaenger()` schickte an die Zusehenden, die Gäste und den Host DER
LAUFENDEN Sendung. Solange nichts läuft, gibt es keinen `host_id` und
niemanden, der „dabei" ist -- der Rundruf ging also an NIEMANDEN.
Ausgerechnet beim Vorbereiten, also genau dann, wenn die beiden
zusammenarbeiten.

Was das angerichtet hätte: VanVan trägt den Donnerstag ein, DogFather
sieht seinen alten Stand, trägt daneben etwas ein -- und einer von
beiden wundert sich später, wo sein Eintrag geblieben ist. Das fällt
erst auf, wenn es weh tut. Die Host-Rollen sind jetzt immer Empfänger,
ABGELEITET aus `HOST_ROLLEN` statt aufgezählt.

DIE FRAGE DAHINTER IST ALLGEMEIN: Ist die Menge im RUHEZUSTAND leer? Ein
Verteiler, der nur im Betrieb gefüllt ist, schweigt genau in der
Vorbereitungsphase -- und die ist die einzige, in der zwei Leute
gleichzeitig an derselben Sache arbeiten. Dieselbe Form wie `every()`
auf einem leeren Feld: Beides sagt Ja, weil nichts da ist.

======================================================================
4. DREI FEHLER IN DEN EIGENEN MESSUNGEN, BEHOBEN
======================================================================

(a) GRÜN AUS DEM FALSCHEN GRUND. Die Messung wartete nach VanVans
    Änderung 1200 ms und fragte dann, ob der getippte Text noch
    dasteht. Er stand da -- weil das Neuzeichnen noch gar nicht
    passiert war. Ein grüner Haken über einer Voraussetzung, die nicht
    eingetreten ist. Jetzt wird auf das EREIGNIS gewartet, nicht auf
    die Uhr, und dass es eingetreten ist, ist eine eigene Zusage.

(b) DIE GEGENPROBE MASS DAS GEGENTEIL. Sie machte `Getippt.binde` zu
    einer LEEREN Funktion -- damit schreibt niemand mehr in das Feld,
    also bleibt der Text erst recht stehen. „Ohne das Gedächtnis"
    heißt nicht „ohne Schreiben", sondern: wieder so wie früher,
    nämlich `feld.value = wert`. Genau das wird jetzt eingesetzt.

(c) ZWEI PRÜFUNGEN LASEN FLIESSTEXT STATT CODE.
    `html.indexOf("reaktion.js")` fand das Wort im Kommentar darüber,
    und `!/localStorage/` schlug an, weil in getippt.js im Kommentar
    steht „liegt im Speicher und NICHT in localStorage". Beide waren
    rot, obwohl der Code stimmte. Jetzt wird der Skript-Einhänger
    verglichen und der Aufruf `localStorage.setItem` gesucht.

UND EINER, DEN NUR DAS BILDSCHIRMFOTO ZEIGEN KONNTE: Ich hatte
`--w-tief`, `--w-hoch` und `--w-ring` als `background` und
`border-color` benutzt -- das sind aber SCHATTEN, keine Farben, und das
steht im Werkstoff-Abschnitt direkt darüber. Tückisch daran: Der
Ersatzwert in `var(--x, fallback)` greift dabei NICHT. Er hilft nur,
wenn die Variable fehlt, nicht wenn ihr Wert für die Eigenschaft
unsinnig ist. Die Sichtumschaltung sah dadurch aus wie zwei Wörter ohne
Knopf.

======================================================================
GEMESSEN
======================================================================

  pruef-reaktion          588, 0 Fehler (vorher 571) -- Abschnitt 22
  mess-regie (neu)         28, 0 Fehler, 3 Bildschirmfotos
  mess-foyer               96, 0 Fehler
  pruef-struktur          102 · pruef-css-klassen 37 · pruef-portnummern 41
  pruef-lesbarkeit         14 · pruef-bewegung 9 · pruef-tippziele 13
  pruef-deutsche-texte     12 · pruef-crew-adresse 173
  pruef-haus-trennung     107 · pruef-community-sicht 10
  pruef-sackgassen         14
  alle 0 Fehler

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-08 15:16:54 +02:00

296 lines
14 KiB
HTML

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>Reaction · Team Dogi</title>
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610081516" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610081516" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610081516" />
<link rel="stylesheet" href="assets/css/start.css?v=202610081516" />
<link rel="stylesheet" href="assets/css/foyer.css?v=202610081516" />
<link rel="stylesheet" href="assets/css/module.css?v=202610081516" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610081516" />
</head>
<body class="start">
<div class="schleier" aria-hidden="true"></div>
<header class="kopfleiste">
<button type="button" class="zurueck-knopf" id="zurueck" hidden>
<svg class="zurueck-knopf__pfeil" viewBox="0 0 24 24" aria-hidden="true">
<path d="M14.5 5.5 8 12l6.5 6.5" />
</svg>
<span class="zurueck-knopf__text">Zurück</span>
</button>
<p class="marke"><span class="marke__text"><a class="zurueck" href="start.html">Team&nbsp;Dogi</a> · Reaction</span></p>
<div class="kopfleiste__rechts">
<span class="wer" id="wer">…</span>
<button type="button" class="abmelden" id="abmelden">Abmelden</button>
</div>
</header>
<!-- ======================================================================
DAS FOYER (08.10.2026)
Filipe: „diese kachel soll nicht mehr geschlossen sein, ABER wenn
man drauf drückt geht zuerst eine seite auf mit
reihen/kategorien. links eine reihe wo die nächsten geplanten
videos schon bereit stehen. wan und umd wie viel uhr … und das
soll geschlossen sein außer für vanvan und dogfather. in der
mitte sollen die leute dan wenn eine sendung läuft drauf drücken
können und der sendung beitreten. auch bei der vorschau schon und
dan rechts will ich die letzte reihe/kategorie, da sollen die
leute alle mir videos anraten können."
---------------------------------------------------------------------
WARUM DAS EINE EIGENE SEITE IST UND KEIN VIERTER ZUSTAND DES SAALS
Der Saal meldet jeden an, der ihn oeffnet („dabei"), baut
Verbindungen auf und haelt den Ereignisstrom offen. Das ist
richtig fuer jemanden, der zusieht -- und falsch fuer jemanden,
der nur nachsehen will, ob heute Abend etwas laeuft. Waere das
Foyer der vierte Zustand derselben Seite, zaehlte jeder Blick
als Zuschauer, und die Zahl im Regiepult saehe nach Publikum
aus, wo niemand zusieht.
Hier wird nur GELESEN. Erst der Knopf in der Mitte fuehrt in den
Saal, und ab da gilt alles, was dort gilt.
---------------------------------------------------------------------
DREI REIHEN, UND DIE MITTE IST DIE BREITESTE
Links der Plan, in der Mitte der Weg hinein, rechts die
Vorschlaege. Die Mitte bekommt mehr Platz, weil sie das Einzige
ist, weswegen die meisten hier sind -- und weil ein Knopf, der
in eine laufende Sendung fuehrt, nicht gesucht werden soll.
AM HANDY STEHT DIE MITTE OBEN. Dort ist „links" nur noch „vor
allem anderen", und das waere die falsche Reihenfolge: Wer am
Telefon nachsieht, ob es laeuft, soll es in der ersten Zeile
sehen und nicht nach zwei Bildschirmhoehen Plan.
====================================================================== -->
<main class="inhalt inhalt--breit" id="foyer">
<section class="kopf-zeile">
<div>
<p class="marke">Reaction</p>
<h1 class="titel">Zusammen schauen</h1>
<p class="unterzeile" id="kopf-satz">Wird geladen …</p>
</div>
</section>
<p class="fehler" id="fehler" role="alert" aria-live="polite" hidden></p>
<div class="foyer-reihen" id="reihen">
<!-- ============ LINKS: DER PLAN ===========================
Nur fuer DogFather und VanVan. Filipe: „und das soll
geschlossen sein außer für vanvan und dogfather."
SIE STEHT GAR NICHT ERST DA, statt leer oder gesperrt zu
sein. Ein Kasten mit Schloss darauf erzaehlt, dass es etwas
zu sehen gaebe -- und genau das soll er nicht. -->
<section class="foyer-reihe foyer-reihe--plan" id="reihe-plan" hidden>
<header class="foyer-reihe__kopf">
<h2 class="foyer-reihe__ueber">Was ansteht</h2>
<span class="foyer-reihe__nur">nur ihr zwei</span>
</header>
<p class="foyer-reihe__satz">
Die Warteschlange aus der Regie — hier nach Termin sortiert.
Was noch keinen hat, steht unten.
</p>
<ol class="plan-liste" id="plan-liste"></ol>
<p class="foyer-reihe__fuss">
Termine setzt du im Saal unter <strong>Regie → Warteschlange</strong>.
</p>
</section>
<!-- ============ MITTE: HINEIN ============================= -->
<section class="foyer-reihe foyer-reihe--saal" id="reihe-saal">
<header class="foyer-reihe__kopf">
<h2 class="foyer-reihe__ueber">Die Sendung</h2>
<span class="foyer-lampe" id="lampe" data-stand="laedt">
<span class="foyer-lampe__punkt" aria-hidden="true"></span>
<span class="foyer-lampe__wort" id="lampe-wort">…</span>
</span>
</header>
<div class="saal-karte" id="saal-karte" data-bild="nein">
<img id="saal-bild" alt="" />
<span class="saal-karte__leer" id="saal-leer">Gerade läuft nichts.</span>
</div>
<strong class="saal-titel" id="saal-titel">—</strong>
<p class="saal-satz" id="saal-satz"></p>
<!-- DER KNOPF IST EIN LINK UND KEIN BUTTON. Wer ihn mit der
mittleren Maustaste antippt, bekommt einen neuen Reiter --
genau das tut man, wenn man die Sendung nebenher laufen
lassen will. Ein `button` mit `location.href` kann das
nicht, und man merkt es erst, wenn es nicht geht. -->
<a class="saal-knopf" id="saal-knopf" href="reaktion.html" hidden>
<span class="saal-knopf__wort" id="saal-knopf-wort">Beitreten</span>
</a>
<p class="saal-zahl" id="saal-zahl" hidden></p>
</section>
<!-- ============ RECHTS: ANRATEN ===========================
Filipe: „da sollen die leute alle mir videos anraten können.
also da können die leute videos reinschicken und nur ich und
vanvan sehen sie."
JEDER DARF SCHICKEN, NIEMAND SIEHT FREMDE. Was man selbst
geschickt hat, steht darunter mit seinem Stand -- sonst
waere das Feld ein Loch, in das man etwas wirft. -->
<section class="foyer-reihe foyer-reihe--raten" id="reihe-raten">
<header class="foyer-reihe__kopf">
<h2 class="foyer-reihe__ueber">Video anraten</h2>
<span class="foyer-reihe__zahl" id="raten-zahl" hidden></span>
</header>
<p class="foyer-reihe__satz" id="raten-satz">
Schick uns ein Video, das wir zusammen schauen sollen.
Sehen tun das nur DogFather und VanVan.
</p>
<form class="raten-form" id="raten-form">
<label class="raten-feld">
<span>YouTube-Adresse</span>
<input type="text" id="raten-video" autocomplete="off"
placeholder="Adresse einfügen" />
</label>
<!-- KEIN `maxlength` HIER. Die Zahl stand sonst zweimal: hier
und als `VORSCHLAG_NOTIZ_MAX` im Server. Wer sie dort
aendert, haette hier die alte stehen gelassen, und der
Zaehler neben „Warum?“ hätte eine Grenze gezählt, die es
nicht mehr gibt. Das Feld lernt sie von der Auskunft
(`notiz_max`); bis dahin schneidet der Server ohnehin ab. -->
<label class="raten-feld">
<span>Warum? <span class="raten-rest" id="raten-rest"></span></span>
<input type="text" id="raten-notiz"
placeholder="Freiwillig — ein Satz reicht" />
</label>
<!-- `knopf` und nicht `knopf-still--haupt`: Der Hauptknopf des
Hauses steht in `gate.css`, und die lädt diese Seite.
`knopf-still--haupt` wird in `reaktion.css` gestaltet --
die gehört dem Saal und wird hier nicht geladen. Die
Klasse hätte also im Quelltext ausgesehen, als sei etwas
hervorgehoben, und auf dem Bildschirm wäre es ein
gewöhnlicher Knopf gewesen. Gefunden hat das
`pruef-css-klassen`. -->
<button type="submit" class="knopf" id="raten-los">Abschicken</button>
</form>
<ol class="raten-liste" id="raten-liste"></ol>
<p class="foyer-reihe__fuss" id="raten-fuss"></p>
</section>
</div>
<!-- ==================================================================
DAS REGIE-TOR (08.10.2026)
Filipe: „unter den [Reihen] fehlt eine kachel, die viel kraesser
und spezieller aussehen soll, wo nur dogfather oder vanvan
reinkommen. mit ihren zugangscodes fuer die seite. und das soll
die regie kachel sein. die muss wirklich ultra krass sein."
Und zur Staerke des Schlosses: „die soll jeder sehen aber nur
vanvan und dogfather sollen da rein kommen bitte."
SIE STEHT FUER ALLE DA -- anders als die linke Reihe, und zwar
auf ausdruecklichen Wunsch. Deshalb steht hier NICHTS drin, was
jemand nicht sehen soll: kein Titel, kein Stand, keine Zahl.
Eine verschlossene Tuer mit einem Schild darauf.
EIN `button` UND KEIN LINK. Der Link in der Mitte fuehrt
woandershin; dieser hier fuehrt zunaechst nirgendwohin, sondern
fragt. Ein `a href` waere ein Versprechen, das er nicht haelt --
und in einem neuen Reiter geoeffnet landete man auf einer Seite,
die sofort zurueckschickt.
================================================================== -->
<button type="button" class="regie-tor" id="regie-tor" data-darf="nein" data-offen="nein">
<span class="regie-tor__licht" aria-hidden="true"></span>
<span class="regie-tor__innen">
<span class="regie-tor__zeichen" aria-hidden="true">
<svg viewBox="0 0 24 24" class="regie-tor__glyphe">
<rect x="4.5" y="10.5" width="15" height="10" rx="2.2" />
<path d="M8 10.5V7.3a4 4 0 0 1 8 0v3.2" />
<circle cx="12" cy="15.4" r="1.5" />
</svg>
</span>
<span class="regie-tor__text">
<span class="regie-tor__ueber">Regie</span>
<strong class="regie-tor__titel" id="regie-titel">Der Platz hinter dem Pult</strong>
<span class="regie-tor__satz" id="regie-satz">…</span>
</span>
<span class="regie-tor__marke" id="regie-marke">Nur ihr zwei</span>
</span>
</button>
</main>
<!-- AUSSERHALB VON `main`, und das ist kein Schoenheitsfehler.
`body.start > main` traegt `position: relative; z-index: 1`, die
Kopfleiste steht bei 6 -- und alles INNERHALB von main wird mit
main zusammen eingeordnet. Ein `z-index: 500` darin bliebe unter
der 6 daneben, und der Vorhang laege unter der Kopfleiste.
Gemessen am 08.10.2026 am Vorraum des Saals, dort war es genau
dieser Fehler. -->
<div class="regie-vorhang" id="regie-vorhang" hidden>
<div class="regie-fenster" role="dialog" aria-modal="true"
aria-labelledby="regie-fenster-titel">
<h2 class="regie-fenster__titel" id="regie-fenster-titel">Regie aufschließen</h2>
<p class="regie-fenster__satz" id="regie-fenster-satz">
Tipp deinen Zugangscode ein — denselben, mit dem du dich hier anmeldest.
</p>
<form id="regie-form">
<label class="regie-feld">
<span>Dein Zugangscode</span>
<!-- `type=password` und nicht `text`: Wer in einer Sendung den
Bildschirm teilt, soll ihn nicht mittippen lassen.
`autocomplete="off"` dazu -- das ist kein Anmeldefeld, und
ein Browser, der hier etwas vorschlaegt, schlaegt das
Falsche vor. -->
<input type="password" id="regie-code" autocomplete="off"
inputmode="text" placeholder="CODE-…" />
</label>
<p class="regie-fenster__fehler" id="regie-fehler" role="alert" hidden></p>
<div class="regie-fenster__knoepfe">
<!-- `knopf-still` und nicht ein erfundenes `knopf-leise`: Der
leise Knopf des Hauses heisst so und steht in `gate.css`,
die diese Seite laedt. Genau diese Falle hat heute schon
einmal zugeschlagen (`knopf-still--haupt`, gestaltet in
reaktion.css, die hier gar nicht geladen wird) -- im
Quelltext sah es nach Gestaltung aus, auf dem Bildschirm
war es ein nackter Browserknopf. -->
<button type="button" class="knopf-still" id="regie-ab">Abbrechen</button>
<button type="submit" class="knopf" id="regie-los">Aufschließen</button>
</div>
</form>
<!-- WAS DIESE TUER LEISTET UND WAS NICHT, STEHT DARUNTER.
Eine Sicherung, die staerker aussieht, als sie ist, ist
schlechter als gar keine: Wer sie fuer mehr haelt, laesst
deswegen etwas anderes weg. -->
<p class="regie-fenster__kleingedruckt">
Das hält einen neugierigen Blick auf, nicht jemanden, der an deinem
offenen Rechner sitzt. Danach bleibt die Regie zwölf Stunden offen.
</p>
</div>
</div>
<!-- bereiche.js VOR kopf.js: Dort steht `window.Bereiche`, und kopf.js
liest daraus die Leitungsrollen. -->
<script src="assets/js/bereiche.js?v=202610081516" defer></script>
<script src="assets/js/wahl.js?v=202610081516" defer></script>
<script src="assets/js/nachfrage.js?v=202610081516" defer></script>
<script src="assets/js/meldung.js?v=202610081516" defer></script>
<script src="assets/js/installieren.js?v=202610081516" defer></script>
<script src="assets/js/kopf.js?v=202610081516" defer></script>
<script src="assets/js/glocke.js?v=202610081516" defer></script>
<script src="assets/js/reaktion-foyer.js?v=202610081516" defer></script>
</body>
</html>