From da7508b6ce562a3a8ff20c7424d704e3c9b87100 Mon Sep 17 00:00:00 2001 From: Dogfather Date: Wed, 7 Oct 2026 13:05:54 +0200 Subject: [PATCH] Vier rote Pruefungen -- drei Phantome und ein echtes Gewicht MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Weiter durch die rote Liste. Drei der vier Befunde zeigten auf gesunden Code; die Pruefungen selbst waren seit einem Umbau nicht nachgezogen worden. pruef-schritt (2 rot -> 71 gruen statt 47) Meldete "1 von 6 Kategorien". Die Karte geht seit dem 01.10.2026 gefiltert auf ("zugetragen"), Bloecke ohne solche Punkte werden gar nicht gezeichnet. Die Pruefung zaehlte danach alle. Schlimmer: Weil dann kein Block mehr zugeklappt war, lief der Klick auf einen zugeklappten 30 Sekunden in die Frist und riss den Lauf mit -- 24 von 71 Pruefungen liefen nie. Jetzt wird erst die Vorgabe geprueft (sie ist eine Zusage), dann auf "Alle" gestellt. pruef-glocke (1 rot -> 34 gruen) Meldete "auf der Anmeldeseite steht der Knopf". Gemessen landete sie auf start.html: Dieselbe Seite war eine Zeile zuvor angemeldet durch zwanzig Arbeitsseiten gelaufen, und angemeldet fuehrt /workspace/ nicht zur Wand. Jetzt eigener Kontext ohne Keks -- und davor die Zeile, dass die Wand wirklich erreicht wurde. pruef-zeichen (1 rot -> 8 gruen) Meldete content: "·" als Kodierungstruemmer. Die Bytes sind C2 B7, also korrektes UTF-8; der Mittelpunkt ist ein begruendeter Trennpunkt (seit 03.10.). Die Regel stammt aus einer Zeit, in der sie selbst festhielt: "KEINE einzige benutzt ein Zeichen aus diesem Block." Jetzt eine benannte Ausnahme MIT Veraltungsschutz: Jedes Zeichen darin muss wirklich vorkommen, sonst wird die Ausnahme rot. pruef-tempo-workspace (1 rot -> 9 gruen) -- DER ECHTE BEFUND uebersicht.html 959 KB, bereich.html?b=live 1094 KB bei einer Grenze von 900. Die Pruefung sagt jetzt auch, WORAN es liegt, und die Antwort war eindeutig: ZWEI Buehnenbilder auf einer Seite, die eines zeigt (281 KB studio + 262 KB portal). Ursache: start.css traegt an body.start::before einen Rueckfall (studio), und kopf.js setzt `data-buehne` erst, wenn die Seite steht. Bis dahin ist der Rueckfall geholt -- auf 43 von 45 Seiten. Jetzt steht die Szene fest im , erzeugt aus bereiche.js (tools/buehne-festschreiben.mjs, 20 Seiten). bereich.html bedient fuenf Bretter und kann das nicht; sie bekommt ein kleines Skript gleich hinter , das die Szene aus `?b=` setzt, bevor gezeichnet wird. Gemessen: 1094 -> 879 KB, und die Grenze haelt wieder. DIE ZUORDNUNG STEHT DAMIT AN ZWEI STELLEN. Das ist nur vertretbar, weil sie nicht auseinanderlaufen kann: pruef-buehnen-fest.mjs (neu, 13 Pruefungen) haelt HTML gegen bereiche.js, prueft die Lage des Skripts und dass jede genannte Szene eine Regel hat. Dabei gefunden und berichtigt -- zweimal an mir selbst: Der erste Entwurf verglich Szenen mit Dateinamen und meldete teamlage.html ("eingang") als Fehler; die Szene gibt es, ihre Regel zeigt nur auf ein crew-Bild. Und der Lage-Anker suchte `data-buehne`, das Skript schreibt `dataset.buehne` -- indexOf gab -1, die Rechnung wurde negativ und meldete "-2276 Zeichen dahinter". Gegenproben gefahren: Szene verschoben -> rot mit Abstand; Ausnahme verwaist -> rot; erfundene Szene -> erkannt. Dazu gruen: css-klassen, workspace-seiten, neue-seiten, haus-trennung, kopfleiste-farbe. Co-Authored-By: Claude Opus 5 --- server/pruef-buehnen-fest.mjs | 183 +++++++++++++++++++++++++++++++ server/pruef-glocke.mjs | 36 +++++- server/pruef-schritt.mjs | 35 ++++++ server/pruef-tempo-workspace.mjs | 14 ++- server/pruef-zeichen.mjs | 41 ++++++- tools/buehne-festschreiben.mjs | 92 ++++++++++++++++ workspace/anleitung.html | 30 ++--- workspace/anruf-probe.html | 10 +- workspace/app.webmanifest | 8 +- workspace/aufgaben.html | 40 +++---- workspace/automation.html | 34 +++--- workspace/befinden.html | 34 +++--- workspace/bereich.html | 79 +++++++++---- workspace/bewerben.html | 38 +++---- workspace/bewerbungen.html | 36 +++--- workspace/buehne.html | 6 +- workspace/calls.html | 36 +++--- workspace/chat.html | 44 ++++---- workspace/content.html | 36 +++--- workspace/crew-index.html | 12 +- workspace/crew.webmanifest | 8 +- workspace/dateien.html | 36 +++--- workspace/entwicklung.html | 36 +++--- workspace/hilfe.html | 30 ++--- workspace/index.html | 10 +- workspace/kalender.html | 36 +++--- workspace/leistung.html | 34 +++--- workspace/manager-ziele.html | 30 ++--- workspace/material.html | 32 +++--- workspace/notizen.html | 28 ++--- workspace/personen.html | 34 +++--- workspace/profil.html | 38 +++---- workspace/reaktion.html | 38 +++---- workspace/rechte.html | 34 +++--- workspace/report.html | 38 +++---- workspace/scouting.html | 34 +++--- workspace/start.html | 38 +++---- workspace/startcheck.html | 36 +++--- workspace/steckbrief.html | 38 +++---- workspace/support.html | 30 ++--- workspace/tafel.html | 6 +- workspace/talente.html | 36 +++--- workspace/team.html | 34 +++--- workspace/teamlage.html | 34 +++--- workspace/teilen.html | 30 ++--- workspace/treff-moderation.html | 32 +++--- workspace/treff-regeln.html | 32 +++--- workspace/uebersicht.html | 34 +++--- workspace/unsere-seiten.html | 30 ++--- workspace/unterstuetzen.html | 32 +++--- workspace/werdegang.html | 32 +++--- workspace/wissen.html | 36 +++--- 52 files changed, 1132 insertions(+), 718 deletions(-) create mode 100644 server/pruef-buehnen-fest.mjs create mode 100644 tools/buehne-festschreiben.mjs diff --git a/server/pruef-buehnen-fest.mjs b/server/pruef-buehnen-fest.mjs new file mode 100644 index 00000000..8b0807d9 --- /dev/null +++ b/server/pruef-buehnen-fest.mjs @@ -0,0 +1,183 @@ +/* ===================================================================== + DIE SZENE STEHT FEST IM DOKUMENT -- und stimmt mit bereiche.js + ===================================================================== + + WARUM ES DIESE PRUEFUNG GIBT (07.10.2026) + + `pruef-tempo-workspace` meldete zwei Seiten ueber 900 KB. Die + Ursache war nicht ein zu grosses Bild, sondern ZWEI: `start.css` + traegt an `body.start::before` einen Rueckfall (das Studio-Bild), + und `kopf.js` setzt `data-buehne` erst, nachdem die Seite steht. + Bis dahin hat der Browser den Rueckfall laengst geholt -- auf 43 + von 45 Seiten, rund 260 KB je Aufruf, fuer ein Bild, das niemand + zu sehen bekommt. + + Seit heute steht die Szene fest im . Damit gibt es die + Zuordnung an ZWEI Stellen: in bereiche.js (wo sie gepflegt wird) + und im Dokument (wo sie frueh genug ankommt). + + EINE ABSCHRIFT MIT WAECHTER IST ETWAS ANDERES ALS EINE ABSCHRIFT. + Diese Datei haelt beide gegeneinander. Wer in bereiche.js eine + Szene aendert und die HTML vergisst, bekommt einen roten Lauf -- + und nicht eine Seite, die seit Wochen das falsche Bild zeigt und + dazu ein zweites laedt. + + NICHT BETROFFEN ist bereich.html: Dort haengt die Szene am + `?b=`-Parameter (vier verschiedene), sie kann also nicht fest im + Dokument stehen. Das steht hier ausdruecklich und nicht als + stille Luecke. + + Aufrufen mit: node server/pruef-buehnen-fest.mjs + ===================================================================== */ +import { readFileSync, existsSync, readdirSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const HIER = dirname(fileURLToPath(import.meta.url)); +const SEITEN = join(HIER, "..", "workspace"); + +let geprueft = 0, fehler = 0; +const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; }; + +/* ---- Die Zuordnung aus bereiche.js --------------------------------- */ +const quelle = readFileSync(join(SEITEN, "assets", "js", "bereiche.js"), "utf8"); +const ausKachel = new Map(); +const mitParameter = new Set(); +for (const m of quelle.matchAll(/ziel:\s*'([^']+)'\s*,\s*szene:\s*'([^']+)'/g)) { + const [, ziel, szene] = m; + if (ziel.includes("?")) { mitParameter.add(ziel.split("?")[0]); continue; } + if (ausKachel.has(ziel) && ausKachel.get(ziel) !== szene) ausKachel.set(ziel, null); + else if (!ausKachel.has(ziel)) ausKachel.set(ziel, szene); +} + +console.log("\n=== 1. Die Zuordnung ist ueberhaupt lesbar ===\n"); +/* DER DRITTE AUSGANG: Faende das Muster nichts, waere alles darunter + trivial gruen -- „keine Abweichung" auf einer leeren Liste. */ +ok(ausKachel.size >= 15, + `${ausKachel.size} Seiten mit fester Szene in bereiche.js gefunden`); +ok(mitParameter.size >= 1, + `und ${mitParameter.size} Seite(n) mit Parameter, die keine feste haben kann ` + + `(${[...mitParameter].join(", ")})`); + +console.log("\n=== 2. Jede dieser Seiten traegt sie im Dokument ===\n"); +let stimmt = 0; +const abweichend = []; +for (const [datei, szene] of ausKachel) { + if (!szene) continue; + const pfad = join(SEITEN, datei); + if (!existsSync(pfad)) { abweichend.push(`${datei}: Datei fehlt`); continue; } + const text = readFileSync(pfad, "utf8"); + const b = text.match(/]*>/); + const steht = b?.[0].match(/data-buehne="([^"]+)"/)?.[1] ?? null; + if (steht === szene) stimmt++; + else abweichend.push(`${datei}: Dokument "${steht}" gegen bereiche.js "${szene}"`); +} +/* DIE ZAHL GEHOERT IN DIE BEDINGUNG -- sonst waere „keine Abweichung" + auch dann wahr, wenn gar nichts geprueft wurde. */ +ok(stimmt >= 15 && abweichend.length === 0, + `${stimmt} Seiten tragen ihre Szene fest und richtig` + + (abweichend.length ? `:\n ` + abweichend.slice(0, 6).join("\n ") : "")); + +console.log("\n=== 2b. Und die Seite mit Parameter setzt sie frueh genug ===\n"); +{ + /* bereich.html bedient fuenf Bretter; welches gemeint ist, steht erst + in der Adresse. Sie traegt deshalb ein kleines Skript GANZ OBEN im + Dokument, das die Szene setzt, bevor gezeichnet wird -- sonst holt + der Browser erst den Rueckfall und dann das richtige Bild + (gemessen: 1094 KB statt 879 KB). + + Die Zuordnung steht damit an zwei Stellen. Hier wird sie + gegeneinander gehalten. */ + const html = readFileSync(join(SEITEN, "bereich.html"), "utf8"); + const ausSeite = new Map([...html.matchAll(/(\w+):\s*"([a-z-]+)"/g)] + .filter((m) => /SZENE\s*=|live|community|technik|schutz|agentur/.test(m[0])) + .map((m) => [m[1], m[2]])); + const ausBereiche = new Map([...quelle.matchAll( + /ziel:\s*'bereich\.html\?b=([a-z]+)'\s*,\s*szene:\s*'([a-z-]+)'/g)] + .map((m) => [m[1], m[2]])); + + ok(ausBereiche.size >= 4, + `bereiche.js nennt ${ausBereiche.size} Bretter mit Szene`); + /* FRUEH GENUG heisst: vor allen anderen Skripten und ohne defer. + Ein `defer` hier waere derselbe Fehler noch einmal, nur eine Ebene + tiefer -- und man saehe ihn nicht. */ + /* `dataset.buehne` und nicht `data-buehne`: Das Skript setzt die + Eigenschaft, es schreibt kein Attribut hin. Mein erster Anker war + die Schreibweise aus dem HTML -- `indexOf` gab -1 zurueck, die + Rechnung wurde negativ, und die Zeile meldete „-2276 Zeichen + dahinter". Ein Suchbegriff, den es nicht gibt, ist kein Befund + ueber die Seite, sondern einer ueber die Pruefung. */ + const stelleSkript = html.indexOf("dataset.buehne"); + const stelleBody = html.indexOf(" stelleBody && stelleSkript - stelleBody < 2000, + `das Skript steht gleich hinter (${stelleSkript - stelleBody} Zeichen dahinter)`); + ok(!/]*\bdefer\b[^>]*>[^<]*dataset\.buehne/s.test(html), + "und nicht mit defer -- sonst kaeme es zu spaet"); + + const uneinig = []; + for (const [brett, szene] of ausBereiche) { + if (ausSeite.get(brett) !== szene) { + uneinig.push(`${brett}: Seite "${ausSeite.get(brett)}" gegen bereiche.js "${szene}"`); + } + } + ok(ausBereiche.size > 0 && uneinig.length === 0, + `alle ${ausBereiche.size} Bretter stimmen mit bereiche.js ueberein` + + (uneinig.length ? `: ${uneinig.join(", ")}` : "")); +} + +console.log("\n=== 3. Der Rueckfall bleibt -- fuer die eine Seite, die ihn braucht ===\n"); +const stil = readFileSync(join(SEITEN, "assets", "css", "start.css"), "utf8"); +ok(/body\.start::before[^}]*background-image:\s*url\(/s.test(stil), + "start.css hat weiterhin einen Rueckfall fuer Seiten ohne feste Szene"); + +console.log("\n=== 4. Gegenprobe: wuerde eine Abweichung auffallen? ===\n"); +{ + /* Dieselbe Rechnung auf einem erfundenen Paar -- ohne sie hiesse + „keine Abweichung" nur, dass der Vergleich nie etwas findet. */ + const erfunden = [["probe.html", "wald", "garage"]]; + const gefunden = erfunden.filter(([, imDokument, inKachel]) => imDokument !== inKachel); + ok(gefunden.length === 1, "ein erfundenes Paar mit zwei Szenen wird als Abweichung erkannt"); + const gleich = [["probe.html", "wald", "wald"]].filter(([, a, b2]) => a !== b2); + ok(gleich.length === 0, "und ein uebereinstimmendes Paar nicht"); +} + +console.log("\n=== 5. Und keine Seite behauptet eine Szene, die es nicht gibt ===\n"); +{ + /* GEPRUEFT WIRD GEGEN DIE REGELN IM STIL, nicht gegen Dateinamen. + + Der erste Entwurf verglich mit `buehne--breit.webp` und + meldete prompt teamlage.html mit „eingang" als Fehler. Der Fund + war falsch und der Weg dorthin lehrreich: Die Szene „eingang" gibt + es sehr wohl -- ihre Regel zeigt nur auf ein `crew-buehne-*`-Bild, + weil diese Seite zum Haus von Team Dogi gehoert. Eine Pruefung, + die vom Dateinamen auf die Gueltigkeit schliesst, kennt die halbe + Wirklichkeit. + + Die richtige Bedingung ist ohnehin eine andere: Eine Szene ist + gueltig, wenn es eine REGEL dafuer gibt. Fehlt sie, greift + stillschweigend der Rueckfall -- die Seite zeigt dann ein fremdes + Bild und niemand merkt es. Genau davor warnt der Kommentar in + teamlage.html. */ + const stilSzenen = new Set([...stil.matchAll(/data-buehne="([a-z-]+)"/g)].map((m) => m[1])); + const unbekannt = []; + let mitSzene = 0; + for (const n of readdirSync(SEITEN).filter((x) => x.endsWith(".html"))) { + const s = readFileSync(join(SEITEN, n), "utf8") + .match(/]*data-buehne="([^"]+)"/)?.[1]; + if (!s) continue; + mitSzene++; + if (!stilSzenen.has(s)) unbekannt.push(`${n}: "${s}" hat keine Regel in start.css`); + } + ok(stilSzenen.size >= 5, + `${stilSzenen.size} Szenen sind im Stil beschrieben (${[...stilSzenen].sort().join(", ")})`); + ok(mitSzene >= 15 && unbekannt.length === 0, + `alle ${mitSzene} Seiten mit fester Szene nennen eine, die es gibt` + + (unbekannt.length ? `: ${unbekannt.join(", ")}` : "")); + /* Gegenprobe: eine erfundene Szene MUSS auffallen. */ + ok(!stilSzenen.has("gibtesnicht"), + "Gegenprobe: eine erfundene Szene stuende nicht in der Liste"); +} + +console.log(`\n${geprueft} Pruefungen`); +console.log(fehler === 0 ? "ALLES IN ORDNUNG" : `${fehler} FEHLER`); +process.exit(fehler ? 1 : 0); diff --git a/server/pruef-glocke.mjs b/server/pruef-glocke.mjs index 5f95f161..665dd6a2 100644 --- a/server/pruef-glocke.mjs +++ b/server/pruef-glocke.mjs @@ -365,10 +365,38 @@ melde("\n=== Der Knopf ist überall ==="); : `auf allen ${gefunden} Seiten mit Kopfleiste`); /* Und NICHT auf der Anmeldeseite -- dort ist niemand angemeldet, - den man benachrichtigen könnte. */ - await seite.goto(BASIS + "/workspace/", { waitUntil: "networkidle" }); - await seite.waitForTimeout(250); - ok(!(await seite.$("#glocke")), "auf der Anmeldeseite steht er nicht"); + den man benachrichtigen könnte. + + ==== IN EINER FRISCHEN SITZUNG (berichtigt 07.10.2026) ========== + + Hier stand `seite.goto(BASIS + "/workspace/")` auf DERSELBEN + Seite, die eine Zeile darüber noch angemeldet durch zwanzig + Arbeitsseiten gelaufen ist. Angemeldet führt `/workspace/` aber + nicht zur Wand, sondern weiter auf `start.html` -- gemessen: + „gelandet auf .../workspace/start.html, Titel: Creator Workspace". + Dort IST eine Glocke, und zwar zu Recht. + + Die Prüfung meldete also seit dem Umbau der Anmeldung einen + Fehler an einer Seite, die sie nie zu Gesicht bekam. Ein + Phantombefund ist schlimmer als keiner: Er zeigt auf etwas + Gesundes, und man sucht dort. + + EIGENER KONTEXT OHNE KEKS -- Kekse hängen am Kontext, nicht an + der Seite; ein `goto` räumt sie nicht weg. Und danach wird + NACHGESEHEN, ob die Wand wirklich erreicht wurde: Eine Prüfung, + die ihre Voraussetzung nicht herstellen kann, muss das sagen + können, statt eine Antwort über die falsche Seite zu geben. */ + const wandKontext = await browser.newContext(); + const wandSeite = await wandKontext.newPage(); + await wandSeite.goto(BASIS + "/workspace/", { waitUntil: "networkidle" }); + await wandSeite.waitForTimeout(250); + const aufDerWand = /\/workspace\/(index\.html)?(\?|$)/.test(wandSeite.url()) + && !!(await wandSeite.$(".rolle")); + ok(aufDerWand, + `die Anmeldewand wurde wirklich erreicht (${wandSeite.url()})`); + ok(aufDerWand && !(await wandSeite.$("#glocke")), + "auf der Anmeldeseite steht er nicht"); + await wandKontext.close(); await browser.close(); } diff --git a/server/pruef-schritt.mjs b/server/pruef-schritt.mjs index 88b43c42..eb64f4ad 100644 --- a/server/pruef-schritt.mjs +++ b/server/pruef-schritt.mjs @@ -383,6 +383,41 @@ melde("=== 8. Im Browser ==="); Geprüft wird nicht, dass es einen Knopf GIBT -- sondern dass er etwas tut, und dass zugeklappt trotzdem dasteht, was drin ist. ================================================================ */ + /* ==== DIE KARTE GEHT SEIT DEM 01.10.2026 GEFILTERT AUF ============ + (nachgezogen am 07.10.2026) + + entwicklung.js setzt beim Oeffnen `stufenFilter = 'zugetragen'`, + sobald dieser Mensch ueberhaupt etwas zugetragen bekommen hat -- + „womit die Karte aufgeht". Bloecke ohne zugetragene Punkte werden + dann gar nicht gezeichnet (`if (!punkte.length) continue`). + + DIESE PRUEFUNG WUSSTE DAVON NICHTS und zaehlte danach ALLE + Kategorien. Ergebnis: „1 von 6" -- ein Befund, der auf die Karte + zeigte, obwohl die Karte tat, was sie soll. Und weil danach kein + einziger Block mehr zugeklappt war, lief der Klick auf einen + zugeklappten 30 Sekunden in die Frist und riss den ganzen Lauf mit. + Ein Fehlalarm, der einen zweiten echten Fehler erzeugt. + + ZUERST DIE VORGABE PRUEFEN -- sie ist eine Zusage, keine Nebensache + --, dann auf „Alle" stellen. Was die Zeile darunter behauptet („die + Karte zeigt ALLE Bloecke"), gilt genau dort. */ + const vorgabe = await seite.evaluate(() => { + const k = document.querySelector('.e-stufen__knopf[aria-pressed="true"]'); + return { + gewaehlt: k?.dataset.erwartung ?? null, + wort: k?.querySelector(".e-stufen__name")?.textContent.trim() ?? null, + zugetragen: Number(document.querySelector('.e-stufen__knopf[data-erwartung="zugetragen"] .e-stufen__zahl')?.textContent || 0), + }; + }); + ok(vorgabe.zugetragen > 0 && vorgabe.gewaehlt === "zugetragen", + `die Karte geht auf „${vorgabe.wort}" auf, weil ${vorgabe.zugetragen} Punkte zugetragen sind`); + + await seite.click('.e-stufen__knopf[data-erwartung=""]'); + await seite.waitForTimeout(600); + const nachAlle = await seite.evaluate(() => + document.querySelector('.e-stufen__knopf[aria-pressed="true"]')?.dataset.erwartung); + ok(nachAlle === "", `„Alle" ist danach gewaehlt (${JSON.stringify(nachAlle)})`); + const bloecke = await seite.evaluate(() => { const b = [...document.querySelectorAll(".e-block")]; return { diff --git a/server/pruef-tempo-workspace.mjs b/server/pruef-tempo-workspace.mjs index 96269cb7..21bb284f 100644 --- a/server/pruef-tempo-workspace.mjs +++ b/server/pruef-tempo-workspace.mjs @@ -224,6 +224,7 @@ for (const datei of SEITEN) { let bytes = 0; let roheBytes = 0, uebertragen = 0; const warten = []; + const brocken = []; const zaehler = (antwort) => { const u = antwort.url(); gesehen.set(u, (gesehen.get(u) || 0) + 1); @@ -247,7 +248,13 @@ for (const datei of SEITEN) { ]); if (!k?.length) return; roheBytes += k.length; - uebertragen += KOMPRIMIERBAR.test(art) ? gzipSync(k).length : k.length; + const wiegt = KOMPRIMIERBAR.test(art) ? gzipSync(k).length : k.length; + uebertragen += wiegt; + /* WAS GENAU WIEGT? (07.10.2026) + „bereich.html?b=live 1094 KB" sagt, DASS etwas zu schwer ist, + und schickt einen danach suchen. Die Antwort liegt hier + ohnehin vor -- sie wurde nur weggeworfen. */ + brocken.push({ u: u.replace(/^https?:\/\/[^/]+/, ""), kb: Math.round(wiegt / 1024) }); } catch { /* nichts zu wiegen */ } })()); }; @@ -383,7 +390,10 @@ for (const datei of SEITEN) { if (!gewogen) nichtGewogen.push(datei); if (anfragen > GRENZE_ANFRAGEN || zaehlwert > GRENZE_UEBERTRAGEN) { - zuGross.push(`${datei} ${anfragen} Anfragen / ${Math.round(zaehlwert / 1024)} KB übertragen`); + const groesste = brocken.sort((x, y) => y.kb - x.kb).slice(0, 3) + .map((x) => `${x.kb} KB ${x.u}`).join(", "); + zuGross.push(`${datei} ${anfragen} Anfragen / ${Math.round(zaehlwert / 1024)} KB übertragen` + + (groesste ? ` -- am schwersten: ${groesste}` : "")); } zeilen.push(` ${datei.padEnd(22)} ${String(anfragen).padStart(3)} Anfragen ` diff --git a/server/pruef-zeichen.mjs b/server/pruef-zeichen.mjs index 0b088d5c..bd656a26 100644 --- a/server/pruef-zeichen.mjs +++ b/server/pruef-zeichen.mjs @@ -234,11 +234,37 @@ console.log("=== 2. Der Server ==="); console.log(""); console.log("=== 3. Zeichen, die CSS auf den Bildschirm schreibt ==="); -/** Truemmerverdacht: Latin-1-Block, wo echte Typografie nie hingehoert. */ -const istTruemmer = (c) => c >= 0x80 && c <= 0xbf; +/* ==== DIE AUSNAHME, DIE NICHT VERALTEN KANN (07.10.2026) ============ + + Der Absatz darueber sagt: „Nachgemessen ueber alle 203 + content-Angaben im Haus: KEINE einzige benutzt ein Zeichen aus + diesem Block." Das stimmte am 03.09.2026 und stimmt seit dem + 03.10.2026 nicht mehr: bereich.css setzt `content: "·"` als + Trennpunkt vor den Aufgabenzaehler -- absichtlich, begruendet, und + als korrektes UTF-8 gespeichert (Bytes C2 B7 nachgemessen). + + DIE REGEL BLEIBT TROTZDEM. Sie hat den echten Fall gefunden (U+00B9, + hochgestellte Eins) und wuerde ihn wieder finden. Was nicht bleiben + darf, ist der Fehlalarm: Eine Pruefung, die bei jedem Lauf eine + richtige Stelle anmeckert, wird weggeklickt -- und nimmt den echten + Fund mit. Das steht vier Absaetze weiter oben in dieser Datei selbst. + + DIE LISTE KANN NICHT ROTTEN, weil sie gegen die Wirklichkeit geprueft + wird: Jedes Zeichen darin MUSS irgendwo wirklich vorkommen. Faellt + der Trennpunkt eines Tages weg, wird die Ausnahme rot statt + stillschweigend weiterzugelten -- dieselbe Bauweise wie bei den + ausgenommenen Dateien in pruef-modi-wortleck. */ +const MIT_ABSICHT = new Map([ + [0x00b7, "Mittelpunkt als Trennzeichen vor dem Aufgabenzaehler (bereich.css)"], +]); + +/** Truemmerverdacht: Latin-1-Block, wo echte Typografie nie hingehoert -- + * bis auf die ausdruecklich benannten Ausnahmen. */ +const istTruemmer = (c) => c >= 0x80 && c <= 0xbf && !MIT_ABSICHT.has(c); { const verdaechtig = []; + const gesehen = new Set(); let stellen = 0; for (const p of geliefert.filter((x) => extname(x) === ".css")) { const text = readFileSync(p, "utf8"); @@ -246,6 +272,7 @@ const istTruemmer = (c) => c >= 0x80 && c <= 0xbf; stellen++; for (const z of m[1]) { const c = z.codePointAt(0); + if (MIT_ABSICHT.has(c)) gesehen.add(c); if (istTruemmer(c)) { verdaechtig.push(`${relative(WURZEL, p)}: content "${m[1]}" enthaelt U+${c.toString(16).toUpperCase().padStart(4, "0")}`); } @@ -253,7 +280,15 @@ const istTruemmer = (c) => c >= 0x80 && c <= 0xbf; } } ok(stellen > 50 && verdaechtig.length === 0, - `${stellen} content-Angaben in CSS, ${verdaechtig.length} mit Kodierungstruemmern`); + `${stellen} content-Angaben in CSS, ${verdaechtig.length} mit Kodierungstruemmern` + + (verdaechtig.length ? ":\n " + verdaechtig.slice(0, 5).join("\n ") : "")); + /* Und die Gegenprobe zur Ausnahme: Was hier steht, muss es geben. */ + const verwaist = [...MIT_ABSICHT.keys()].filter((c) => !gesehen.has(c)); + ok(verwaist.length === 0, + `alle ${MIT_ABSICHT.size} benannten Ausnahmen kommen wirklich vor` + + (verwaist.length + ? ` -- verwaist: ${verwaist.map((c) => "U+" + c.toString(16).toUpperCase()).join(", ")}` + : "")); for (const s of verdaechtig.slice(0, 12)) console.log(" " + s); /* GEGENPROBE: Wuerde sie den echten Fall sehen? Nicht theoretisch -- diff --git a/tools/buehne-festschreiben.mjs b/tools/buehne-festschreiben.mjs new file mode 100644 index 00000000..fafcd5ab --- /dev/null +++ b/tools/buehne-festschreiben.mjs @@ -0,0 +1,92 @@ +/* ===================================================================== + buehne-festschreiben.mjs — die Szene gehört in die HTML (07.10.2026) + ===================================================================== + + WAS GEMESSEN WURDE, BEVOR ES DIESES WERKZEUG GAB: + + `pruef-tempo-workspace` meldete zwei Seiten über der Grenze von + 900 KB (uebersicht.html 959 KB, bereich.html?b=live 1094 KB). Die + Prüfung sagt seit heute auch, WORAN es liegt, und die Antwort war + eindeutig: + + 281 KB buehne-studio-breit.webp + 262 KB buehne-portal-breit.webp + + ZWEI Bühnenbilder auf einer Seite, die nur eines zeigt. Der Grund + steht in start.css: `body.start::before` trägt als Rückfall das + Studio-Bild, und `kopf.js` setzt `data-buehne` erst, nachdem die + Seite steht. Bis dahin hat der Browser den Rückfall längst geholt. + + Das betrifft nicht eine Seite, sondern 43 von 45: Nur material.html + und teamlage.html schreiben ihre Szene fest -- und zwar mit genau + dieser Begründung, die dort schon steht („steht hier fest und nicht + im Skript"). + + --------------------------------------------------------------------- + WAS DIESES WERKZEUG TUT + + Es liest die Zuordnung „Seite -> Szene" aus bereiche.js -- der + Stelle, an der sie ohnehin gepflegt wird -- und schreibt sie als + `data-buehne` in das -Element der jeweiligen Seite. + + DIE ZWEITE STELLE IST KEIN WIDERSPRUCH ZUR HAUSREGEL, solange sie + nicht auseinanderlaufen kann: `pruef-buehnen-fest.mjs` hält beide + gegeneinander und wird rot, sobald sich eine ändert. Eine Abschrift + mit Wächter ist etwas anderes als eine Abschrift. + + NICHT BETROFFEN ist bereich.html: Dort hängt die Szene am + `?b=`-Parameter (vier verschiedene), sie kann also nicht fest im + Dokument stehen. Diese eine Seite holt weiterhin zuerst den + Rückfall -- dafür bleibt der Rückfall auch stehen. + + AUFRUF: node tools/buehne-festschreiben.mjs [--probe] + ===================================================================== */ + +import { readFileSync, writeFileSync, existsSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const HIER = dirname(fileURLToPath(import.meta.url)); +const WURZEL = join(HIER, ".."); +const SEITEN = join(WURZEL, "workspace"); +const nurProbe = process.argv.includes("--probe"); + +/* Die Zuordnung kommt aus bereiche.js. Gelesen wird mit einem Muster + und nicht per Import: Die Datei ist für den Browser gebaut (IIFE, + kein Export), und ein Modul daraus zu machen wäre ein Umbau am + Lieferstand für ein Werkzeug. */ +const quelle = readFileSync(join(SEITEN, "assets", "js", "bereiche.js"), "utf8"); +const zuordnung = new Map(); +for (const m of quelle.matchAll(/ziel:\s*'([^']+)'\s*,\s*szene:\s*'([^']+)'/g)) { + const [, ziel, szene] = m; + /* Seiten mit Fragezeichen hängen am Parameter -- siehe oben. */ + if (ziel.includes("?")) continue; + const vorhanden = zuordnung.get(ziel); + if (vorhanden && vorhanden !== szene) { + console.log(` !! ${ziel}: zwei Szenen (${vorhanden} und ${szene}) -- übersprungen`); + zuordnung.set(ziel, null); + continue; + } + if (!vorhanden) zuordnung.set(ziel, szene); +} + +let gesetzt = 0, schonDa = 0, fehlt = 0; +for (const [datei, szene] of zuordnung) { + if (!szene) continue; + const pfad = join(SEITEN, datei); + if (!existsSync(pfad)) { console.log(` ?? ${datei} gibt es nicht`); fehlt++; continue; } + let text = readFileSync(pfad, "utf8"); + + const body = text.match(/]*)>/); + if (!body) { console.log(` ?? ${datei}: kein `); fehlt++; continue; } + if (/data-buehne="/.test(body[1])) { schonDa++; continue; } + + const neu = ``; + text = text.replace(body[0], neu); + if (!nurProbe) writeFileSync(pfad, text); + console.log(` ${nurProbe ? "waere" : "gesetzt"}: ${datei.padEnd(22)} ${szene}`); + gesetzt++; +} + +console.log(`\n${gesetzt} gesetzt, ${schonDa} standen schon fest, ${fehlt} nicht möglich` + + (nurProbe ? " (nur Probe, nichts geschrieben)" : "")); diff --git a/workspace/anleitung.html b/workspace/anleitung.html index 2ee4e6f8..7710eb67 100644 --- a/workspace/anleitung.html +++ b/workspace/anleitung.html @@ -7,20 +7,20 @@ - - - - - + + + + + - - + + - + @@ -289,16 +289,16 @@ Und anleitung.js braucht es doppelt: Symbol, Farbe, Name und Adresse jeder Karte kommen von dort, nicht vom Server. --> - + - - - - - - + + + + + + diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index b51fcae3..0cc578b3 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`. --> - - + + - +