Die Zeitraeume bekommen eine Tafel -- und die Zahl, die fehlte
Filipe: "das muss viel geiler aussehen bitte. ich will dass es auch seeeehr gut erkennbar ist, genau wie der text drueber. mach das bitte alles so dass es mega speziell, hochwertig und lesbar ist." DAS EIGENTLICHE PROBLEM WAR LESBARKEIT, nicht Geschmack. Im Bildschirmfoto gemessen: Ueberschrift und Erklaersatz standen direkt auf der Buehne -- einem Foto mit hellrotem Berg. Und die Karte griff nach `--flaeche-tief`, einer Variablen, die es im ganzen Haus nicht gibt; gegriffen hat der Ersatzwert mit 72 % Deckung, also kam der Berg mit. Eine Variable, die nirgends steht, faellt nicht auf. Jetzt sitzt alles in EINEM Koerper: vierfarbige Fassung, deckendes Innenglas mit Messraster, die Zeitraeume als eingelassene Felder mit dunklen Fugen. Das ist nicht neu erfunden, sondern die Rollenkachel der Anmeldeseite -- ein Haus, eine Handschrift. Typenschild und Hauptzahl in gebuerstetem Metall, mit vollwertigem Rueckfall. "GUELTIGE LIVE-GEHEN-TAGE" WURDE BIS HEUTE WEGGEWORFEN. Datenbank, Anzeigefeld und Importzeile waren da -- nur `spaltenRaten` hatte kein Muster dafuer. Am 14.09. wurde das alte `/tag/` reparirt, das die Spalte faelschlich zur Datumsspalte machte; die Reparatur hat den falschen Empfaenger entfernt und keinen richtigen bestellt. Die Pruefdatei SCHICKTE den Wert seit dem 14.09. und hat nie nachgesehen, ob er ankommt. Jetzt steht er als Streifen da, in genau so viele Kaestchen geteilt, wie der Zeitraum Tage hat. ZWEI KLASSEN IM WAEHLER, mit Grund: `.spannen__schild` allein (0,1,0) kam gegen `.inhalt .feldschild` aus start.css (0,2,0) nicht an -- im Browser gemessen war weder `display: flex` noch die Farbe da. UND EINE ZEITBOMBE ENTSCHAERFT: pruef-backstage-import rechnete "morgen" mit `toISOString()` (UTC), der Server mit Ortszeit. Um 00:40 Berlin war das hier berechnete "morgen" in Wahrheit HEUTE -- die Gegenprobe "ein Datum in der Zukunft wird abgelehnt" fiel taeglich zwischen Mitternacht und zwei Uhr um. Alle drei Tage werden jetzt aus einer Stelle abgeleitet. Gemessen statt behauptet: 43 258 Bildpunkte hinter Ueberschrift und Satz abgetastet, kein einziger rot. Gegenprobe mit weggenommenem Innenglas: 2 857 rote -- die Messung kann Rot also sehen. pruef-backstage-import 133 (war 127), pruef-xlsx 75 (war 67), pruef-css-klassen, pruef-leistung-optik 59, pruef-leistung: alle gruen. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -40,9 +40,26 @@ const BASIS = `http://127.0.0.1:${PORT}`;
|
||||
const ordner = mkdtempSync(join(tmpdir(), "ws-backstage-"));
|
||||
const DB = join(ordner, "workspace.db");
|
||||
const jetzt = new Date().toISOString();
|
||||
const heute = new Date().toISOString().slice(0, 10);
|
||||
const gestern = new Date(Date.now() - 86400000).toISOString().slice(0, 10);
|
||||
const morgen = new Date(Date.now() + 86400000).toISOString().slice(0, 10);
|
||||
/* HEUTE IST DER TAG, DEN DER SERVER MEINT -- nicht der in Greenwich.
|
||||
16.09.2026, 00:40 Berlin: `toISOString()` sagte "2026-09-15", der
|
||||
Server (heuteLokal(), Ortszeit) sagte "2026-09-16". Damit war das
|
||||
hier berechnete "morgen" in Wahrheit HEUTE, und die Gegenprobe
|
||||
"ein Datum in der Zukunft wird abgelehnt" schlug fehl -- an der
|
||||
Zeitzone, nicht am Code. Zwischen Mitternacht und zwei Uhr morgens
|
||||
lag diese Pruefung also taeglich zwei Stunden lang falsch.
|
||||
|
||||
Eine Zeitbombe genau der Sorte, die schon einmal notiert wurde
|
||||
(Shop, 06.09.2026): Die Wanduhr darf Eingabe sein, nie Annahme.
|
||||
ALLE DREI WERDEN AUS EINER STELLE ABGELEITET -- drei eigene
|
||||
Rechnungen waeren drei Gelegenheiten, wieder auseinanderzulaufen. */
|
||||
const tagLokal = (versatz) => {
|
||||
const d = new Date(Date.now() + versatz * 86400000);
|
||||
const p2 = (n) => String(n).padStart(2, "0");
|
||||
return `${d.getFullYear()}-${p2(d.getMonth() + 1)}-${p2(d.getDate())}`;
|
||||
};
|
||||
const heute = tagLokal(0);
|
||||
const gestern = tagLokal(-1);
|
||||
const morgen = tagLokal(1);
|
||||
|
||||
let gemacht = 0, fehler = 0;
|
||||
const ok = (b, text) => {
|
||||
@@ -782,6 +799,135 @@ console.log("\n=== Die Seite ===");
|
||||
`und die Spanne groesser als ein Nebenwert (${groessen.spanne} px)`);
|
||||
ok(/\d/.test(groessen.text), `die Hauptzahl traegt Diamanten ("${groessen.text}")`);
|
||||
|
||||
/* ---- "11 VON 13 TAGEN LIVE" (16.09.2026) ------------------------
|
||||
Die Spalte "Gültige LIVE-Gehen-Tage" steht seit dem 14.09. in
|
||||
dieser Pruefdatei -- und wurde die ganze Zeit weggeworfen, weil
|
||||
`spaltenRaten` kein Muster dafuer hatte. Die Pruefung hat den Wert
|
||||
zwar GESCHICKT, aber nie nachgesehen, ob er ankommt.
|
||||
|
||||
GEMESSEN WIRD DER ANTEIL IN PIXELN, nicht das Vorhandensein des
|
||||
Streifens: Ein Streifen, der immer voll ist, zeigt keine Zahl,
|
||||
sondern eine Dekoration. 11 von 13 sind 85 %. */
|
||||
const lauf = await seite.evaluate(() => {
|
||||
const bahn = document.querySelector(".spanne__lauf-bahn");
|
||||
const teil = document.querySelector(".spanne__lauf-teil");
|
||||
if (!bahn || !teil) return null;
|
||||
return {
|
||||
anteil: Math.round((teil.getBoundingClientRect().width
|
||||
/ bahn.getBoundingClientRect().width) * 100),
|
||||
text: document.querySelector(".spanne__lauf-text")?.textContent || "",
|
||||
};
|
||||
});
|
||||
ok(!!lauf, "der Streifen der gueltigen Tage steht auf der Karte");
|
||||
ok(lauf && lauf.text.includes("11 von 13 Tagen live"),
|
||||
`er nennt die Zahl im Klartext ("${lauf?.text}")`);
|
||||
ok(lauf && Math.abs(lauf.anteil - 85) <= 2,
|
||||
`und ist zu ${lauf?.anteil} % gefuellt (11 von 13 = 85 %)`);
|
||||
|
||||
/* ---- DER EIGENTLICHE WUNSCH: LESBARKEIT (16.09.2026) ------------
|
||||
Filipe: "ich will dass es auch seeeehr gut erkennbar ist, genau
|
||||
wie der text drüber."
|
||||
|
||||
Hinter dem Inhalt dieser Seite liegt die Buehne -- ein Foto mit
|
||||
einem HELLROTEN Berg. Ueberschrift und Erklaersatz standen bis
|
||||
heute direkt darauf.
|
||||
|
||||
GEMESSEN WIRD AN ECHTEN BILDPUNKTEN, nicht an der Farbangabe im
|
||||
Stil: Die weiss nichts davon, was hinter ihr liegt. Das Foto ist
|
||||
stark rot, das Innenglas blaeulich-dunkel (#0b111c: R=11, B=28) --
|
||||
ein Bildpunkt mit R deutlich ueber B kann dort also nur vom Foto
|
||||
stammen.
|
||||
|
||||
WAS BEIM ERSTEN ANLAUF SCHIEFGING, und es ist lehrreich: Die
|
||||
Messung meldete zehn rote Bildpunkte -- und hatte recht. Sie kamen
|
||||
aber nicht vom Foto, sondern vom ANSCHLAG des Typenschilds, den
|
||||
drei roten Kerben, die ich selbst dorthin gesetzt habe. Eine
|
||||
Messung, die "alles Rote ist das Foto" annimmt, misst irgendwann
|
||||
die eigene Gestaltung. Der Anschlag wird deshalb ausgespart -- und
|
||||
zwar mit seiner EIGENEN Breite aus dem Stil, nicht mit einer
|
||||
abgeschriebenen Zahl. */
|
||||
const roteBildpunkte = async (b64) => {
|
||||
const bild = new Image();
|
||||
bild.src = "data:image/png;base64," + b64;
|
||||
await bild.decode();
|
||||
const c = document.createElement("canvas");
|
||||
c.width = bild.width; c.height = bild.height;
|
||||
const g = c.getContext("2d");
|
||||
g.drawImage(bild, 0, 0);
|
||||
const skala = bild.width / window.innerWidth;
|
||||
|
||||
const schild = document.querySelector(".spannen__schild");
|
||||
const satz = document.querySelector(".spannen__satz");
|
||||
if (!schild || !satz) return null;
|
||||
|
||||
/* Der Anschlag ist ein ::before und hat kein eigenes Rechteck.
|
||||
Seine Breite und der Abstand dahinter stehen aber im Stil --
|
||||
daraus wird die Stelle abgeleitet, ab der die Zeile beginnt. */
|
||||
const vor = getComputedStyle(schild, "::before");
|
||||
const st = getComputedStyle(schild);
|
||||
const versatz = (parseFloat(vor.width) || 0) + (parseFloat(st.columnGap) || 0) + 2;
|
||||
|
||||
const sr = schild.getBoundingClientRect();
|
||||
const zr = satz.getBoundingClientRect();
|
||||
const felder = [
|
||||
{ name: "Ueberschrift", l: sr.left + versatz, t: sr.top, w: sr.width - versatz, h: sr.height },
|
||||
{ name: "Erklaersatz", l: zr.left, t: zr.top, w: zr.width, h: zr.height },
|
||||
];
|
||||
|
||||
let punkte = 0, rot = 0;
|
||||
for (const f of felder) {
|
||||
const w = Math.max(1, Math.round(f.w * skala));
|
||||
const h = Math.max(1, Math.round(f.h * skala));
|
||||
const p = g.getImageData(Math.round(f.l * skala), Math.round(f.t * skala), w, h).data;
|
||||
for (let i = 0; i < p.length; i += 4) {
|
||||
punkte++;
|
||||
if (p[i] > p[i + 2] + 20) rot++;
|
||||
}
|
||||
}
|
||||
return { punkte, rot };
|
||||
};
|
||||
|
||||
const durchsicht = await seite.evaluate(roteBildpunkte,
|
||||
(await seite.screenshot({ type: "png" })).toString("base64"));
|
||||
|
||||
ok(durchsicht && durchsicht.punkte > 5000,
|
||||
`hinter Ueberschrift und Erklaersatz wurden ${durchsicht?.punkte} Bildpunkte abgetastet`);
|
||||
ok(durchsicht && durchsicht.rot === 0,
|
||||
`kein einziger davon traegt das Rot des Fotos (${durchsicht?.rot})`);
|
||||
|
||||
/* Das Bild der fertigen Tafel -- MIT Glas, also so, wie Filipe sie
|
||||
sieht. Es entsteht hier und nicht am Ende: Am Ende ist ein anderer
|
||||
Creator geoeffnet, dessen Zeitraum keine gueltigen Tage hat. */
|
||||
if (process.env.SCHIRM) {
|
||||
await seite.locator("#spannen-block").screenshot(
|
||||
{ path: join(HIER, "pruef-spannen.png") });
|
||||
}
|
||||
|
||||
/* GEGENPROBE -- und sie ist hier das Wichtigste.
|
||||
|
||||
"Null rote Bildpunkte" koennte auch heissen, dass gar kein Foto
|
||||
dahinterliegt, dass das Fenster zu klein ist oder dass ich die
|
||||
falsche Stelle abtaste. Also wird das Innenglas fuer einen
|
||||
Augenblick weggenommen und werden DIESELBEN Stellen mit
|
||||
DEMSELBEN Abtaster noch einmal gemessen: Kommt das Rot dann durch,
|
||||
war es vorher das Glas, das es aufgehalten hat -- und die Messung
|
||||
kann ueberhaupt Rot sehen.
|
||||
|
||||
Genau die Sorte Pruefung, die ohne Gegenprobe jahrelang gruen
|
||||
bleibt und nichts beweist. */
|
||||
await seite.addStyleTag({ content:
|
||||
".spannen-tafel { background: none !important; }"
|
||||
+ ".spannen-tafel::before { display: none !important; }"
|
||||
+ ".spannen, .spanne { background: none !important; }" });
|
||||
await seite.waitForTimeout(250);
|
||||
const ohneGlas = await seite.evaluate(roteBildpunkte,
|
||||
(await seite.screenshot({ type: "png" })).toString("base64"));
|
||||
ok(ohneGlas && ohneGlas.rot > 200,
|
||||
`ohne das Innenglas kommt das Foto sofort durch (${ohneGlas?.rot} rote Bildpunkte) `
|
||||
+ "-- die Messung kann Rot also sehen");
|
||||
await seite.reload({ waitUntil: "networkidle" });
|
||||
await seite.waitForTimeout(1500);
|
||||
|
||||
/* ---- EINE WOCHE OHNE EINEN EINZIGEN TAG (14.09.2026) ------------
|
||||
Filipe: "wieso werden die zahlen oben nicht ausgefuellt von der
|
||||
excel datei?"
|
||||
@@ -851,11 +997,6 @@ console.log("\n=== Die Seite ===");
|
||||
"mit einem einzigen Tag sind die Karten wieder da");
|
||||
|
||||
|
||||
if (process.env.SCHIRM) {
|
||||
await seite.locator("#spannen-block").screenshot(
|
||||
{ path: join(HIER, "pruef-spannen.png") });
|
||||
}
|
||||
|
||||
await seite.click("#netz-zu").catch(() => {});
|
||||
await seite.waitForTimeout(300);
|
||||
/* Gemessen wird SATZ FUER SATZ, nicht der Block. Ein Block, der
|
||||
|
||||
@@ -201,6 +201,30 @@ console.log("\n=== Vom Excel bis zur Spaltenzuordnung ===");
|
||||
`"${name}" ${sollTreffen ? "ist" : "ist NICHT"} die Datumsspalte (erkannt: ${trifft})`);
|
||||
}
|
||||
|
||||
/* ---- UND WO LANDET SIE DANN? (16.09.2026) ----------------------
|
||||
Die Gegenprobe oben sagte nur, dass "Gültige LIVE-Gehen-Tage"
|
||||
KEINE Datumsspalte ist. Das war richtig -- und unvollständig: Die
|
||||
Spalte hatte danach ueberhaupt keinen Empfaenger mehr und lief ins
|
||||
Leere. Datenbankspalte, Anzeigefeld und Importzeile waren da, nur
|
||||
das Muster fehlte.
|
||||
|
||||
DAS IST DIE GENAUE SORTE FEHLER, DIE EINE GEGENPROBE ALLEIN NICHT
|
||||
FINDET: "trifft das Falsche nicht" ist kein Beweis dafuer, dass es
|
||||
das Richtige trifft. Deshalb steht beides jetzt nebeneinander. */
|
||||
ok(tabelle[0][zu.gueltige_tage] === "Gültige LIVE-Gehen-Tage",
|
||||
`"Gültige LIVE-Gehen-Tage" landet als gueltige_tage (Spalte ${zu.gueltige_tage})`);
|
||||
ok(zahlLesen(tabelle[1][zu.gueltige_tage]) === 11,
|
||||
`und traegt die Zahl ${zahlLesen(tabelle[1][zu.gueltige_tage])}`);
|
||||
for (const [name, soll] of [
|
||||
["Gültige LIVE-Gehen-Tage", true], ["Valid go LIVE days", true],
|
||||
["Tage seit dem Beitritt", false], ["Livestreams", false],
|
||||
["Diamanten", false], ["LIVE-Dauer", false],
|
||||
]) {
|
||||
const trifft = spaltenRaten([name]).gueltige_tage === 0;
|
||||
ok(trifft === soll,
|
||||
`"${name}" ${soll ? "ist" : "ist NICHT"} die Spalte der gueltigen Tage (erkannt: ${trifft})`);
|
||||
}
|
||||
|
||||
/* Und die Werte, die dahinter herauskommen. */
|
||||
ok(zahlLesen(tabelle[1][zu.diamanten]) === 12480,
|
||||
`"12.480" wird zu ${zahlLesen(tabelle[1][zu.diamanten])} und nicht zu 12,48`);
|
||||
|
||||
@@ -762,6 +762,27 @@ export function spaltenRaten(kopf) {
|
||||
"Daten" die englische Silbe "date" steckt. Auf Zufall soll das
|
||||
nicht beruhen. */
|
||||
tag: /datum|date|zeitraum|^\s*tage?\s*$|^\s*day\s*$/i,
|
||||
/* "Gültige LIVE-Gehen-Tage" (16.09.2026).
|
||||
|
||||
DIESE SPALTE STEHT IN JEDER BACKSTAGE-AUSGABE UND WURDE BIS
|
||||
HEUTE WEGGEWORFEN. Die Datenbank hat die Spalte, die Anzeige hat
|
||||
das Feld, der Import hat die Zeile -- nur hier fehlte das
|
||||
Muster, und damit blieb `gueltige_tage` auf dem ganzen Weg leer.
|
||||
Aufgefallen ist es nicht beim Lesen des Codes, sondern beim
|
||||
Nachsehen, warum "Gültige Tage" auf der Karte nie erschien.
|
||||
|
||||
WARUM ES VORHER NICHT AUFFIEL: Bis zum 14.09. hat das Muster
|
||||
`tag: /.../` mit einem blossen `/tag/` genau diese Spalte
|
||||
gefressen und als DATUMSSPALTE verbucht (Wert: "11"). Als das
|
||||
repariert wurde, fiel die Spalte durchs Raster -- die Reparatur
|
||||
hat den falschen Empfänger entfernt, aber keinen richtigen
|
||||
bestellt.
|
||||
|
||||
Es trifft die deutsche und die englische Schreibweise. Kein
|
||||
anderes Muster oben oder unten greift daneben: "dauer_min"
|
||||
verlangt `live` ZUSAMMEN mit `zeit`, und `tag` nur ganze
|
||||
Wörter. */
|
||||
gueltige_tage: /g(?:ü|ue)ltig.*tag|valid.*day|live.*geh|go.*live.*day/i,
|
||||
diamanten: /diamant|diamond/i,
|
||||
dauer_min: /dauer|duration|minut|zeit.*live|live.*zeit/i,
|
||||
zuschauer_avg: /(durchschnitt|avg|average|⌀|ø).*(zuschauer|viewer)|(zuschauer|viewer).*(durchschnitt|avg)/i,
|
||||
|
||||
Reference in New Issue
Block a user