From e272dd46734f5a058d82802a41e3d1512aea4bec Mon Sep 17 00:00:00 2001 From: Dogfather Date: Tue, 29 Sep 2026 12:56:03 +0200 Subject: [PATCH] Am Handy ist die Transportleiste wieder erreichbar Der offene Punkt von gestern Nacht, jetzt geloest -- und dabei stellte sich heraus, dass er schlimmer war als gemeldet. WAS WIRKLICH LOS WAR Gemeldet war: "Am Handy bleibt bei offener Regie vom Bild nichts." Gemessen auf 390x844 ergab sich mehr: Saal 69..844 (775 px) Zeilen 101 + 0 + 511 + 325 = 937 transport 681..1006 -- 162 Pixel UNTER dem Fensterrand Der Koerper hat `overflow: hidden`; die Leiste war also nicht abgeschnitten, sondern weg. Play, "Naechstes" und die Sprungknoepfe: bei offener Regie nicht erreichbar. Das Bild hatte dabei null Pixel. DIE RECHNUNG, DIE ES ERKLAERT Regieleiste 101 (zwei Zeilen Register) Regie-Kopf 207 (fuenf Zeilen zu je rund 44 px -- der Klapp-Knopf stand allein in einer) Regie-Inhalt 305 Transport 325 (Schiene 48 + drei Gruppen untereinander) ---- 938 von 775. Video, Regie und Transport passen auf einem Handy nicht nebeneinander. Das ist keine Frage der Gestaltung, sondern des Platzes. VIER SCHNITTE UND EINE ENTSCHEIDUNG 1. Die Tempo-Gruppe klappt hinter ihren eigenen Wert -- dasselbe Muster wie "Aa" im Chat, das dort 73 Pixel gespart hat. Der Knopf zeigt "1x" oder "1,5x", man muss zum Nachsehen also nicht aufklappen. Am Rechner gibt es ihn nicht. 2. Der Klapp-Knopf rutscht neben die Lampe statt in eine eigene Zeile. 3. Die drei Gruppen der Transportreihe stehen nebeneinander (158 statt 240 Pixel) -- moeglich, seit die Tempo-Gruppe klappt. 4. Die Messwerte stehen in einer Zeile, die grossen Knoepfe bekommen weniger Polsterung. Das reichte nicht. Also die Entscheidung: AM HANDY LEGT SICH DIE REGIE UEBER DAS BILD, statt ihm Platz wegzunehmen -- als Rasterfeld ueber die Zeilen 2 und 3, unten angedockt, hoechstens 72 Prozent hoch. Oben bleibt ein Streifen Bild stehen (gemessen 135 px): Wer mitten in der Sendung etwas einstellt, muss sehen, worueber er redet. Gemessen jetzt: Saal 775 = Leiste 101 + Bild 481 + Regie 346, und der Transport steht bei 601..844 -- erreichbar. DREI VERSUCHE, DIE ES NICHT WURDEN (und warum) - `position: absolute` mit gemessener Transporthoehe: lief, lag aber 17 Pixel ueber den Registern. Der Bezugsrahmen eines absolut gesetzten RASTERFELDES ist sein Rasterbereich, nicht der Container -- mit `grid-row: 3` (0 Pixel hoch) wurde aus `max-height: 100%` eine Hoehe von einem Pixel. - `top` UND `bottom` UND `height: fit-content`: Der Browser verwirft dann das `bottom`; die Regie ragte 101 Pixel in die Leiste. - `grid-row: 2 / 4` mit `align-self: end` statt absolut: ragte 146 Pixel nach oben und schob die Seite 198 Pixel breiter. AUF 320 PIXELN BLEIBT ES BEIM ALTEN Dort passen Register (104), Regie-Kopf (210) und Transportleiste (158) zusammen nicht in die 499 Pixel Saal -- es fehlten 17. Fuenf Verteilungen haben nur bestimmt, WER verdeckt wird: erst "Vorbereiten" und "Beenden" unter der Leiste, dann die Schublade ueber "Gaeste" und "Bild". Die Schublade greift deshalb erst ab 360 Pixeln; darunter bleibt der Stand von vorher -- ein bekannter Mangel, aber kein neuer. Der Grund steht im Stilblatt. NEBENBEI GEFUNDEN - `.muenzsatz` brach nicht um und ragte auf 320 px 8 Pixel hinaus -- die Tafel rollte dort wieder waagerecht. WACHEN GESCHAERFT - Die Klammerwache von gestern hat heute ihren ersten echten Fund gemacht: Beim Herausschneiden einer Regel blieb eine `}` stehen. Ohne sie waere das als drei Befunde in `mess-reaktion` aufgetaucht, die wie Programmfehler ausgesehen haetten. - Der Namensstreit-Waechter meldete `pult (grid vs. flex)` und `messwerte__paar (grid vs. flex)` -- beides Fehlalarm: Eine Regel in einem Zusammenhang (`.saal .pult`) und eine in einer Medienabfrage sind Varianten, kein Streit. Er nimmt beides jetzt aus. Dabei fiel auf, dass sein Muster die schliessende Klammer verbrauchte, die der naechste Treffer als Anfang braucht -- der echte Fall vom 28.09. (`.stufen` zweimal) wurde dadurch gar nicht gefunden. Die Gegenprobe deckt jetzt fuenf Faelle ab. - `mess-reaktion` mass die HOEHE der Kinozeile und haette 431 Pixel gemeldet, waehrend das Bild vollstaendig verdeckt war. Sie misst jetzt, wieviel oberhalb der Schublade uebrig bleibt. GEPRUEFT pruef-reaktion, -css-klassen, -tippziele, -handy, -struktur, -buehne: alle EXIT 0 mess-reaktion EXIT 0, kein ACHTUNG, kein offener Punkt mess-quer EXIT 0 -- fuenf Groessen, nichts rollt seitlich mess-buehne EXIT 0 --- server/mess-quer.mjs | 11 ++ server/mess-reaktion.mjs | 46 +++++- server/pruef-reaktion.mjs | 74 +++++++--- workspace/anruf-probe.html | 10 +- workspace/app.webmanifest | 8 +- workspace/assets/css/reaktion.css | 231 +++++++++++++++++++++++++++++- workspace/assets/js/reaktion.js | 27 ++++ workspace/aufgaben.html | 38 ++--- workspace/automation.html | 32 ++--- workspace/befinden.html | 34 ++--- workspace/bereich.html | 44 +++--- workspace/bewerben.html | 38 ++--- workspace/bewerbungen.html | 36 ++--- workspace/buehne.html | 2 +- workspace/calls.html | 34 ++--- workspace/chat.html | 40 +++--- 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/material.html | 32 ++--- workspace/notizen.html | 28 ++-- workspace/personen.html | 32 ++--- workspace/profil.html | 36 ++--- workspace/reaktion.html | 46 +++--- workspace/rechte.html | 34 ++--- workspace/report.html | 36 ++--- workspace/scouting.html | 32 ++--- workspace/start.html | 32 ++--- workspace/startcheck.html | 34 ++--- workspace/steckbrief.html | 36 ++--- workspace/support.html | 28 ++-- workspace/tafel.html | 6 +- 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 | 30 ++-- workspace/wissen.html | 34 ++--- 50 files changed, 1044 insertions(+), 689 deletions(-) diff --git a/server/mess-quer.mjs b/server/mess-quer.mjs index 3c0e8749..be2f7c98 100644 --- a/server/mess-quer.mjs +++ b/server/mess-quer.mjs @@ -310,6 +310,7 @@ const GERAETE = [ ["Handy", 412, 915], ["iPhone", 390, 844], ["kleines Handy", 360, 640], + ["kleinstes Handy", 320, 568], ]; let befunde = 0; @@ -374,6 +375,16 @@ for (const [gname, breite, hoehe] of GERAETE) { const register = await host.seite.evaluate(() => [...document.querySelectorAll(".reiter__knopf")].map((b) => b.dataset.tafel)); console.log(` Register: ${register.join(" · ")}`); + { + const z = await host.seite.evaluate(() => { + const saal = document.getElementById("saal"); + const teile = [...saal.children].filter((e) => e.getBoundingClientRect().height > 0) + .map((e) => { const q = e.getBoundingClientRect(); + return `${e.id || e.className.split(" ")[0]} ${Math.round(q.top)}..${Math.round(q.bottom)}`; }); + return `Zeilen ${getComputedStyle(saal).gridTemplateRows} | ${teile.join(" / ")}`; + }); + console.log(` ${z}`); + } /* Erst die Seite als Ganzes. */ const seitlich = await host.seite.evaluate(() => diff --git a/server/mess-reaktion.mjs b/server/mess-reaktion.mjs index 765c5918..4bfb3400 100644 --- a/server/mess-reaktion.mjs +++ b/server/mess-reaktion.mjs @@ -2456,15 +2456,55 @@ await zuschauer.kontext.close(); const alle = [...document.getElementById("saal").children] .filter((e) => e.getBoundingClientRect().height > 0) .map((e) => `${e.id || e.className.split(" ")[0]}=${Math.round(e.getBoundingClientRect().height)}`); + /* WIEVIEL VOM BILD IST WIRKLICH ZU SEHEN? + Die Hoehe der Kinozeile sagt das nicht: Seit die Regie am + Handy eine Schublade darueber ist, kann die Zeile 431 Pixel + hoch sein und trotzdem vollstaendig verdeckt. Ein gruener + Haken ueber eine Zeilenhoehe waere genau die Sorte, die das + Falsche misst. Gefragt ist der Streifen, der oberhalb der + Schublade uebrig bleibt. */ + const kino = document.getElementById("teil-live").getBoundingClientRect(); + const regie = document.getElementById("pult").getBoundingClientRect(); + const frei = Math.max(0, Math.round(Math.min(kino.bottom, regie.top) - kino.top)); return { saal: g("saal"), leiste: g("regieleiste"), kino: g("teil-live"), pult: g("pult"), - alle: alle.join(" + ") }; + frei, alle: alle.join(" + ") }; }); console.log(` Handy offen: Saal ${platzHandy.saal} = Leiste ${platzHandy.leiste} ` + `+ Kino ${platzHandy.kino} + Regie ${platzHandy.pult}`); console.log(` Handy Zeilen: ${platzHandy.alle}`); - if (platzHandy.kino < 190) { + const spur = await chef.seite.evaluate(() => { + const saal = document.getElementById("saal"); + const r = saal.getBoundingClientRect(); + const teile = [...saal.children].filter((e) => e.getBoundingClientRect().height > 0) + .map((e) => { + const q = e.getBoundingClientRect(); + return `${e.id || e.className.split(" ")[0]} ${Math.round(q.top)}..${Math.round(q.bottom)}`; + }); + const tr = document.querySelector(".transport"); + const teileT = [...(tr ? tr.children : [])].map((e) => { + const q = e.getBoundingClientRect(); + return `${e.className.split(" ")[0]}=${Math.round(q.height)}`; + }); + return `Saal ${Math.round(r.top)}..${Math.round(r.bottom)} (Fenster ${window.innerHeight})` + + ` | Zeilen ${getComputedStyle(saal).gridTemplateRows}` + + ` | ${teile.join(" / ")}` + + ` | Transport: ${teileT.join(" + ")}` + + ` | Regie-Kopf: ${(() => { + const l = document.querySelector(".pult__leiste"); + if (!l) return "-"; + const kinder = [...l.children].map((e) => { + const q = e.getBoundingClientRect(); + return `${e.className.split(" ")[0] || e.tagName.toLowerCase()}` + + ` ${Math.round(q.top)}..${Math.round(q.bottom)}`; + }); + return `${Math.round(l.getBoundingClientRect().height)} px = ${kinder.join(" / ")}`; + })()}`; + }); + console.log(` SPUR: ${spur}`); + console.log(` Vom Bild sichtbar (nicht von der Regie verdeckt): ${platzHandy.frei} px`); + if (platzHandy.frei < 120) { offenePunkte.push(`Am Handy bleibt bei offener Regie vom Bild fast nichts ` - + `(${platzHandy.kino} px, noetig 190). Bekannt seit 28.09.2026. ` + + `(${platzHandy.frei} px sichtbar, noetig 120). Bekannt seit 28.09.2026. ` + `Wurzel: die umgebrochene Transportleiste braucht auf 320 px ` + `Breite 220 Pixel. Der Weg steht in reaktion.css beim Handy-Block.`); } diff --git a/server/pruef-reaktion.mjs b/server/pruef-reaktion.mjs index 3d214f0b..f978f45d 100644 --- a/server/pruef-reaktion.mjs +++ b/server/pruef-reaktion.mjs @@ -989,12 +989,39 @@ melde("=== 12. Die Regie ==="); Grundregeln, die beide sagen, WIE das Bauteil gebaut ist -- `grid` gegen `flex`, wie bei `.stufen`. */ const positiv = (wert) => wert && wert !== "none" && !/^none/.test(wert); + + /* ZWEI EINSCHRAENKUNGEN MEHR, BEIDE AM 29.09.2026 NOETIG GEWORDEN. + + (3) Der Selektor muss GENAU die Klasse sein. `.saal .pult { + display: flex }` ist keine zweite Grundregel zu `.pult { + display: grid }`, sondern dieselbe Klasse in einem anderen + Zusammenhang -- und genau dafuer sind Selektoren da. Die + erste Fassung nahm jede Klasse, der ein Trennzeichen + vorausging, und meldete deshalb `pult (grid vs. flex)`. + + (4) Regeln INNERHALB einer Medienabfrage zaehlen nicht. Sie + sind per Definition eine Variante fuer eine andere + Umgebung -- `messwerte__paar` steht am Fingergeraet als + `flex` und sonst als `grid`, und das ist die Absicht. + + Was uebrig bleibt, ist der Fall, um den es ging: `.stufen` + stand zweimal als blanke Grundregel, ohne Medienabfrage und + ohne Zusammenhang -- zwei verschiedene Bauteile, ein Name. */ + const ohneMedien = (text) => text.replace( + /@media[^{]*\{(?:[^{}]|\{[^{}]*\})*\}/g, + (m) => m.replace(new RegExp("[^" + String.fromCharCode(10) + "]", "g"), " ")); + const lies = (text) => { const raus = {}; - for (const m of text.matchAll(/(^|[\s};])(\.(-?[a-zA-Z_][a-zA-Z0-9_-]*))\s*\{([^{}]*)\}/g)) { - const wert = (m[4].match(/(?:^|[\s;])display\s*:\s*([^;]+)/) || [, ""])[1].trim(); + /* LOOKBEHIND STATT VERBRAUCHEN. Mit `(^|[};])` frisst der erste + Treffer die schliessende Klammer, die der naechste als Anfang + braeuchte -- `.stufen { … } .stufen { … }` lieferte dann nur + EINEN Treffer, und die Gegenprobe zum echten Fall vom 28.09. + schlug fehl. */ + for (const m of ohneMedien(text).matchAll(/(?:^|(?<=[};]))\s*(\.(-?[a-zA-Z_][a-zA-Z0-9_-]*))\s*\{([^{}]*)\}/g)) { + const wert = (m[3].match(/(?:^|[\s;])display\s*:\s*([^;]+)/) || [, ""])[1].trim(); if (!positiv(wert)) continue; - (raus[m[3]] ||= []).push(wert); + (raus[m[2]] ||= []).push(wert); } return raus; }; @@ -1010,20 +1037,33 @@ melde("=== 12. Die Regie ==="); : `${Object.keys(grundregeln).length} Klassen sagen, WIE sie gebaut sind ` + "-- keine davon zweimal verschieden"); - /* GEGENPROBE: Findet die Regel einen eingeschobenen Streit? Ohne - diese Zeile waere oben nur bewiesen, dass sie „in Ordnung" - sagen kann. Eingeschoben wird genau der Fall vom 28.09.: eine - zweite Grundregel mit einem anderen positiven display. */ - ok(streitIn(roh("reaktion.css") + " .pult-tafel { display: flex; } ") - .some((z) => z.startsWith("pult-tafel (")), - "Gegenprobe: eine zweite Grundregel mit anderem display faellt auf"); - - /* UND IN DIE ANDERE RICHTUNG: Ein Bauteil, das erst versteckt und - dann gezeigt wird, darf NICHT anschlagen -- sonst haette man - die Regel nur so lange geschaerft, bis sie alles meldet. */ - ok(streitIn(" .probe { display: none; } .probe { display: grid; } ").length === 0 - && streitIn(" .probe { display: grid; } .probe { display: flex; } ").length === 1, - "Gegenprobe: verstecken-und-zeigen ist kein Streit, grid-gegen-flex schon"); + /* GEGENPROBEN AN EIGENSTAENDIGEN TEXTEN, nicht an die echte + Datei angehaengt. Die erste Fassung tat das -- und scheiterte, + sobald der Sucher Medienabfragen ausklammert: Was hinter dem + letzten `@media` angehaengt wird, kann darin verschwinden. + Eine Gegenprobe, die vom Inhalt der geprueften Datei abhaengt, + prueft nicht die Regel, sondern die Datei. */ + { + const faelle = [ + ["der Fall vom 28.09. (zwei Grundregeln, anderes display)", + ".stufen { display: grid; } .stufen { display: flex; }", true], + ["verstecken und zeigen", + ".probe { display: none; } .probe { display: grid; }", false], + ["dieselbe Klasse in einem Zusammenhang", + ".pult { display: grid; } .saal .pult { display: flex; }", false], + ["eine Variante in einer Medienabfrage", + ".paar { display: grid; } @media (pointer: coarse) { .paar { display: flex; } }", false], + ["zweimal dasselbe display", + ".gleich { display: grid; } .gleich { display: grid; }", false], + ]; + const daneben = faelle + .filter(([, text, soll]) => (streitIn(text).length > 0) !== soll) + .map(([name]) => name); + ok(daneben.length === 0, + daneben.length ? `Gegenprobe daneben bei: ${daneben.join(", ")}` + : `Gegenprobe: alle ${faelle.length} Faelle richtig eingeordnet ` + + "(Streit, Zustand, Zusammenhang, Medienabfrage, Wiederholung)"); + } /* ---- Nichts rollt absichtlich waagerecht ---------------------- diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index 7a2c171b..487a394a 100644 --- a/workspace/anruf-probe.html +++ b/workspace/anruf-probe.html @@ -16,8 +16,8 @@ verdeckt. Gefunden hat es pruef-struktur. Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. --> - - + + - +