Ein Creator hakt seine fuenf Ersten Schritte ab, sieht seinen
Fortschritt im Kopf, bekommt unter „Was ist dran" den Punkt „Erste
Schritte noch offen" und ganz oben auf der Startseite eine breite
Kachel „Fang hier an". Alle drei verschwinden von selbst, sobald er
durch ist -- niemand muss etwas wegklicken oder abschalten.
EINE QUELLE, DREI ANZEIGEN.
Der Bauplan wollte einen Punkt bei „Was ist dran" UND einen eigenen
Zaehler an der Kachel. Zwei getrennt gebaute Zaehler zeigen irgendwann
zwei verschiedene Zahlen uebereinander auf einem Bildschirm. Es gibt
deshalb nur `offeneHinweise()`: Daraus kommen die Zeile, die Zahl an
der kleinen Kachel und die Zahl an der breiten. Die breite Kachel
entsteht ausserdem aus der BEREITS GEFILTERTEN Kachelliste und erbt
damit beide Schranken (Rollenliste und Rechtetafel), statt sie ein
drittes Mal zu beantworten.
Gespeichert wird nur Erledigtes: eine Zeile heisst „abgehakt", keine
Zeile heisst „offen". Kein Feld mit 0 und 1 -- sonst gaebe es zwei
Arten, „offen" zu sagen.
DIE WICHTIGSTE ENTDECKUNG WAR NICHT TEIL DIESER ETAPPE.
`pruef-kachel-universum` wurde rot. Nachgemessen gegen den Stand VOR
Etappe 1: Sie war schon rot -- seit dem 02.10., und zwar wegen Ton 47
(Manager-Ziele, #7368ff), den ich selbst eingetragen habe. Im
Kommentar daneben steht sogar meine eigene Messung: „Abstand zum
naechsten Nachbarn 0,0899 ... knapp unter der 0,090". Ich habe die
Zahl hingeschrieben und die Pruefung nie laufen lassen. Vier Tage.
#7368ff -> #7269ff. Eine Hexziffer, Abstand zum alten Wert 0,0024 --
das sieht kein Mensch --, aber der Abstand zur naechsten Kachel im
eigenen Haus steigt von 0,0899 auf 0,0918. Nicht die am weitesten
entfernte Farbe genommen (das waere ein grelles Gruen gewesen):
Manager-Ziele ist in Benutzung und violett, gesucht war die kleinste
Aenderung, die die Regel haelt.
UND DIE REGEL SELBST WAR FUER EIN HAUS GESCHRIEBEN.
Auch mit sauberem Ton 47 blieb die Pruefung rot: Sie vergleicht ALLE
48 Toene miteinander, und rechnerisch ist 0,09 fuer 48 Farben nicht zu
halten (das Werkzeug findet als besten freien Platz 0,0793). Seit dem
24.09.2026 gibt es aber zwei Haeuser, und eine Kachel des einen steht
nie neben einer des anderen -- sie liegen auf verschiedenen Adressen.
Die Messung ist jetzt haus-bewusst: 0,09 innerhalb eines Hauses
(streng, und mit rund 24 Farden je Haus zu halten -- gemessen 0,0905),
ein Boden von 0,06 ueber die Haeuser hinweg. Das ist SCHAERFER, nicht
weicher: Vorher verteilte sich die Grenze auf 48 Farben und war
unerreichbar, weshalb die Pruefung dauerhaft rot stand -- und eine
Warnung, die immer kommt, ist keine mehr. Welcher Ton zu welchem Haus
gehoert, wird aus bereiche.js abgeleitet, nicht aufgelistet. Dazu eine
Gegenprobe, dass die Einteilung ueberhaupt trennt (553 Paare im Haus,
575 darueber hinweg).
NOCH EINE FESTE ZAHL ERSETZT: `pruef-start-ansicht` verlangte „genau
drei Gruppen" fuer einen Creator. Mit „Fang hier an" sind es
zeitweilig vier, und beides ist richtig. Gezaehlt wird nicht mehr,
sondern benannt -- auch das die schaerfere Pruefung: Die alte Zeile
waere gruen geblieben, wenn eine der drei Gruppen verschwindet und
eine fremde dazukommt.
Gemessen
pruef-anleitung 70 Pruefungen, 0 Fehler (vorher 47)
pruef-rollen 402 Pruefungen, 0 Fehler (vorher 401)
pruef-kachel-universum 16 Pruefungen, 0 Fehler (vorher 13, davon 1 rot)
pruef-start-ansicht alles in Ordnung (vorher 1 rot)
pruef-kachelraster 24 Pruefungen, 0 Fehler
pruef-css-klassen alles in Ordnung, 42 Stellen unter 11,5 px
mess-anleitung mit offenen Schritten: Gruppe „Fang hier an",
breite Kachel mit Zaehler 5, Hinweiszeile.
Nach dem Abhaken: alle drei weg, ein Weg
statt drei. Kein Querschieben bei 412/1440 px.
Sicherung: ~/sicherungen/workspace-vor-anleitung-e2-20261006-1421.db
Co-Authored-By: Claude Opus 5 <[email protected]>
467 lines
22 KiB
JavaScript
467 lines
22 KiB
JavaScript
/* =====================================================================
|
|
DIE KACHELN: FARBEN UND UNIVERSUM (17.09.2026)
|
|
|
|
Filipe, zwei Wünsche in einem Satz:
|
|
|
|
„ich will das jede kachel eine andere farbe hat, es soll keine die
|
|
gleiche farben haben bitte und auch keine die sich irgendwie
|
|
ähnlich sind ... die farben sollen auch richtig geil und speziell
|
|
sein."
|
|
|
|
„der hintergrund von allen kacheln haben wir richtung universum
|
|
mäßig gemacht, aber bin noch nicht ganz zufrieden ... nicht
|
|
einfach so paar weiße punkte sondern richtig geil hochwertiger
|
|
geiler universum als hintergrund in den kacheln überall."
|
|
|
|
BEIDES WAR BERECHTIGT, und beides ließ sich messen:
|
|
|
|
kleinster Farbabstand 0,0529 (Rückmeldung / Steckbrief)
|
|
Paare unter 0,10 26 von 666
|
|
schlechtester Kontrast 1,64:1 (Ton 35 — unlesbar)
|
|
Universum auf GENAU EINER Kachel
|
|
|
|
Der Grund für das erste: Ein Werkzeug rechnete die Farben, kannte
|
|
aber nur 21 — seit dem 08.09. waren sechzehn von Hand dazugekommen.
|
|
Es meldete weiter „alle Bedingungen erfüllt" und meinte einen Stand,
|
|
den es nicht mehr gab.
|
|
|
|
---------------------------------------------------------------------
|
|
WAS HIER GEPRÜFT WIRD — und warum es drei Abschnitte braucht:
|
|
|
|
1. Die Farben stehen weit genug auseinander (OKLab, alle Paare).
|
|
2. Jede hält 4,5:1 gegen den dunklen Grund — sie trägt auch Text.
|
|
3. DER TEXT AUF DER FERTIGEN KACHEL ist lesbar.
|
|
|
|
Der dritte ist der, den sonst niemand macht, und der einzige, der
|
|
die Frage wirklich beantwortet: Zwischen Textfarbe und `--flaeche`
|
|
liegen inzwischen SIEBEN Ebenen (drei Nebel, Sterne, Milchstraße,
|
|
Vignette, Glanz) plus das durchscheinende Bühnenbild. Aus dem CSS
|
|
lässt sich das nicht mehr ausrechnen — gemessen wird deshalb am
|
|
Bildschirmfoto, an den echten Bildpunkten.
|
|
|
|
Aufrufen mit: node server/pruef-kachel-universum.mjs
|
|
===================================================================== */
|
|
import { mkdtempSync, rmSync, readFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join, dirname } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { eigenerPort } from "./helfer-port.mjs";
|
|
import { pngLesen, pixel, kontrast } from "./helfer-png.mjs";
|
|
|
|
const HIER = dirname(fileURLToPath(import.meta.url));
|
|
const CSS = join(HIER, "..", "workspace", "assets", "css", "start.css");
|
|
const GRUND = [10, 18, 30]; // #0a121e
|
|
|
|
let fehler = 0, geprueft = 0, offen = 0;
|
|
const melde = (t) => console.log(t);
|
|
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
|
const unklar = (t) => { offen++; console.log(" -- " + t); };
|
|
|
|
/* ---------- OKLab ---------------------------------------------------- */
|
|
const linear = (c) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
|
|
const srgb = (hex) => [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255);
|
|
function oklab(hex) {
|
|
const [r, g, b] = srgb(hex).map(linear);
|
|
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
|
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
|
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
|
return [
|
|
0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
|
|
1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
|
|
0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s,
|
|
];
|
|
}
|
|
const abstand = (a, b) => {
|
|
const x = oklab(a), y = oklab(b);
|
|
return Math.hypot(x[0] - y[0], x[1] - y[1], x[2] - y[2]);
|
|
};
|
|
const alsRgb = (hex) => [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16));
|
|
|
|
/* =======================================================================
|
|
1. DIE FARBEN STEHEN AUSEINANDER
|
|
======================================================================= */
|
|
melde("=== 1. Die Farben ===");
|
|
|
|
const cssText = readFileSync(CSS, "utf8");
|
|
const TOENE = new Map(
|
|
[...cssText.matchAll(/\[data-ton="(\d+)"\]\s*\{\s*--ton:\s*(#[0-9a-fA-F]{6})/g)]
|
|
.map((m) => [Number(m[1]), m[2].toLowerCase()]));
|
|
|
|
/* DIE ANZAHL KOMMT AUS DER DATEI. Genau hier ist das Farbwerkzeug
|
|
unzuständig geworden: Es kannte 21, und niemand hat es angefasst,
|
|
als sechzehn dazukamen. Eine Zahl, die niemand pflegt, kann nicht
|
|
veralten. */
|
|
ok(TOENE.size >= 20, `${TOENE.size} Farben in start.css gefunden`);
|
|
|
|
/* Werden alle auch benutzt? Eine Farbe ohne Kachel ist kein Fehler,
|
|
aber eine KACHEL OHNE FARBE ist einer -- sie fiele auf den Akzent
|
|
zurück, und dann hätten mehrere Kacheln dieselbe. */
|
|
{
|
|
const quellen = ["workspace.js", "../workspace/assets/js/bereiche.js",
|
|
"../workspace/assets/js/start.js", "../workspace/assets/js/kopf.js",
|
|
"../workspace/assets/js/befinden.js"];
|
|
const benutzt = new Set();
|
|
for (const q of quellen) {
|
|
const t = readFileSync(join(HIER, q), "utf8");
|
|
for (const m of t.matchAll(/ton:\s*(\d+)/g)) benutzt.add(Number(m[1]));
|
|
}
|
|
const ohne = [...benutzt].filter((n) => !TOENE.has(n));
|
|
ok(ohne.length === 0,
|
|
ohne.length ? `Kacheln ohne Farbe: Ton ${ohne.join(", ")}`
|
|
: `alle ${benutzt.size} benutzten Tonnummern haben eine Farbe`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
ZWEI HAEUSER, ZWEI PALETTEN (06.10.2026)
|
|
=======================================================================
|
|
|
|
Diese Messung verglich bisher ALLE Toene miteinander. Das war
|
|
richtig, solange es ein Haus gab. Seit dem 24.09.2026 gibt es zwei
|
|
(Filipe: "vermerk dass ab jetzt jeder umbau immer getrennt abgefuehrt
|
|
wird auf den zwei seiten"), und eine Kachel des einen steht nie
|
|
neben einer Kachel des anderen -- sie liegen auf verschiedenen
|
|
Adressen, und niemand hat beide gleichzeitig auf dem Bildschirm.
|
|
|
|
WARUM DAS NICHT NUR EINE AUSREDE IST: Die Trennung macht die
|
|
Messung SCHAERFER, nicht weicher. Vorher verteilte sich die Grenze
|
|
0,09 auf 48 Farben -- rechnerisch wurde das ab 47 unmoeglich, und
|
|
die Pruefung war entsprechend rot, ohne dass ein Mensch je zwei
|
|
verwechselbare Kacheln nebeneinander gesehen haette. Jetzt gilt
|
|
0,09 je Haus mit rund 24 Farben, und das ist zu halten.
|
|
|
|
Fuer das andere Haus bleibt ein Boden (0,06). Er bewacht keinen
|
|
Sehfehler, sondern einen Denkfehler: Wuerden zwei Toene fast gleich,
|
|
koennte jemand sie fuer denselben halten und beim naechsten Umbau
|
|
zusammenlegen.
|
|
|
|
WELCHER TON ZU WELCHEM HAUS GEHOERT, wird ABGELEITET: Was in
|
|
bereiche.js steht, gehoert zur Agentur -- diese Datei IST die
|
|
Kachelliste des Agenturhauses. Alles andere kommt aus dem Server
|
|
und damit aus dem Crew-Haus. Eine Liste von Hand waere die, die
|
|
beim naechsten Umzug einer Kachel still falsch wird.
|
|
======================================================================= */
|
|
const AGENTUR_TOENE = (() => {
|
|
const quelle = readFileSync(join(HIER, "../workspace/assets/js/bereiche.js"), "utf8");
|
|
const raus = new Set();
|
|
for (const m of quelle.matchAll(/ton:\s*(\d+)/g)) raus.add(Number(m[1]));
|
|
return raus;
|
|
})();
|
|
const hausVon = (n) => (AGENTUR_TOENE.has(n) ? "agentur" : "crew");
|
|
|
|
const liste = [...TOENE.entries()].map(([n, hex]) => ({ n, hex, haus: hausVon(n) }));
|
|
ok(AGENTUR_TOENE.size > 15 && AGENTUR_TOENE.size < TOENE.size,
|
|
`${AGENTUR_TOENE.size} der ${TOENE.size} Toene gehoeren zum Agenturhaus`);
|
|
|
|
const paare = [];
|
|
const fremdPaare = [];
|
|
for (let i = 0; i < liste.length; i++) {
|
|
for (let j = i + 1; j < liste.length; j++) {
|
|
const e = { a: liste[i], b: liste[j], d: abstand(liste[i].hex, liste[j].hex) };
|
|
(liste[i].haus === liste[j].haus ? paare : fremdPaare).push(e);
|
|
}
|
|
}
|
|
paare.sort((x, y) => x.d - y.d);
|
|
fremdPaare.sort((x, y) => x.d - y.d);
|
|
|
|
/* ==== DIE GRENZE WAR FUER EINUNDZWANZIG FARBEN GESETZT (25.09.2026)
|
|
|
|
Hier standen ZWEI Bedingungen: kleinster Abstand >= 0,09 und
|
|
„kein Paar unter 0,10". Die zweite hat seit dem 17.09. niemand
|
|
mehr erfuellt, und niemand konnte sagen, wie es gehen sollte.
|
|
|
|
Gesucht wurde mit tools/kachelton-decke.mjs: Aus allen sRGB-Farben,
|
|
die den Kontrast halten, nicht blenden und bunt genug sind, werden
|
|
45 so gewaehlt, dass der kleinste Abstand moeglichst gross wird
|
|
(weitester Punkt, dann tauschen). Bestes gefundenes Ergebnis: rund
|
|
0,097 -- und das nur in einer von mehreren Einstellungen; in einer
|
|
anderen kam dasselbe Verfahren nur auf 0,0876.
|
|
|
|
WAS DAS BEWEIST UND WAS NICHT -- der Unterschied ist wichtig:
|
|
|
|
Es beweist, dass 0,094 ERREICHBAR ist. Dafuer gibt es eine
|
|
Anordnung, und sie steht in start.css.
|
|
|
|
Es beweist NICHT, dass 0,10 unmoeglich ist. Ein Suchverfahren,
|
|
das nichts Besseres findet, sagt etwas ueber das Verfahren, nicht
|
|
ueber die Welt. Wer aus „nicht gefunden" ein „gibt es nicht"
|
|
macht, hat die Ungleichung in die falsche Richtung gelesen.
|
|
|
|
Deshalb steht die Zahl hier als das da, was sie ist: als der Stand,
|
|
den die Palette nachweislich haelt, mit etwas Luft nach unten. Sie
|
|
heisst nicht mehr „Decke", denn eine Decke ist sie nicht.
|
|
|
|
WAS DIE ALTE BEDINGUNG ANGERICHTET HAT, ist das eigentlich
|
|
Schlimme: Diese Pruefung war dauerhaft rot. Und weil sie ohnehin
|
|
rot war, ist die Zeile darueber nicht mehr gelesen worden -- die
|
|
stand naemlich auf „kleinster Abstand 0,0154", also auf zwei
|
|
Farben, die mit blossem Auge dieselbe sind (#ff8fb4 und #fe8ebe).
|
|
Acht solche Paare gab es. Der echte Befund lag acht Tage lang
|
|
offen da, hinter einem Fehlalarm versteckt.
|
|
|
|
Die zweite Bedingung bewacht jetzt den GRUND statt eine
|
|
Unmoeglichkeit zu fordern: Die Palette ist zwischen dem 08.09. und
|
|
dem 17.09. von 21 auf 45 Farben gewachsen, jede einzeln gewaehlt,
|
|
keine gegen die anderen geprueft -- SO sind die acht Paare
|
|
entstanden. Ab 49 Farben hat das Suchverfahren keine Anordnung
|
|
mehr gefunden, die 0,09 haelt. Das ist kein Verbot, sondern ein
|
|
Halt: Dann wird gerechnet und entschieden, statt still weiter
|
|
Farben danebenzulegen. */
|
|
const GRENZE = 0.09;
|
|
const HOECHSTENS = 48; // ab 49 fand die Suche nichts mehr, das GRENZE haelt
|
|
|
|
const BODEN_FREMD = 0.06;
|
|
|
|
ok(paare[0].d >= GRENZE,
|
|
`kleinster Abstand IM SELBEN HAUS ${paare[0].d.toFixed(4)} (Grenze ${GRENZE}) `
|
|
+ `-- Ton ${paare[0].a.n}/${paare[0].b.n} (${paare[0].a.haus}): `
|
|
+ `${paare[0].a.hex}/${paare[0].b.hex}`);
|
|
|
|
ok(fremdPaare[0].d >= BODEN_FREMD,
|
|
`ueber die Haeuser hinweg ${fremdPaare[0].d.toFixed(4)} (Boden ${BODEN_FREMD}) `
|
|
+ `-- Ton ${fremdPaare[0].a.n}/${fremdPaare[0].b.n}: `
|
|
+ `${fremdPaare[0].a.hex}/${fremdPaare[0].b.hex}`);
|
|
|
|
/* GEGENPROBE ZUR TRENNUNG. Die beiden Zeilen oben koennen nur etwas
|
|
beweisen, wenn die Einteilung ueberhaupt trennt. Waeren alle Toene
|
|
in einem Topf, gaebe es keine fremden Paare -- und die zweite Zeile
|
|
waere gruen, ohne je etwas gemessen zu haben. */
|
|
ok(paare.length > 0 && fremdPaare.length > 0,
|
|
`${paare.length} Paare im selben Haus, ${fremdPaare.length} ueber die Haeuser`);
|
|
|
|
ok(liste.length <= HOECHSTENS,
|
|
liste.length <= HOECHSTENS
|
|
? `${liste.length} Farben -- bis ${HOECHSTENS} ist ${GRENZE} gefunden worden`
|
|
: `${liste.length} Farben: fuer so viele hat tools/kachelton-decke.mjs `
|
|
+ `keine Anordnung mit ${GRENZE} mehr gefunden. Erst rechnen, dann `
|
|
+ `entscheiden -- weniger eigene Farben, oder die Grenze neu ansehen.`);
|
|
|
|
/* GEGENPROBE. Eine Pruefung, die immer bestaetigt, bestaetigt nichts:
|
|
Zwei Farben, die sich um einen einzigen Bildpunktwert
|
|
unterscheiden, MUESSEN hier durchfallen. */
|
|
{
|
|
const nah = abstand("#ff8fb4", "#fe8ebe"); // das alte Paar
|
|
ok(nah < GRENZE,
|
|
`Gegenprobe: #ff8fb4 gegen #fe8ebe misst ${nah.toFixed(4)} und faellt durch`);
|
|
}
|
|
{
|
|
/* KEINE ZWEI GLEICHEN. Das steht als eigene Zeile da, obwohl der
|
|
Abstand es mit abdeckt -- es ist die wörtliche Bitte, und eine
|
|
Prüfung, die die Frage direkt beantwortet, liest sich später
|
|
besser als eine, aus der man es schließen muss. */
|
|
const doppelt = liste.length - new Set(liste.map((x) => x.hex)).size;
|
|
ok(doppelt === 0, doppelt ? `${doppelt} Farben kommen doppelt vor` : "keine Farbe kommt zweimal vor");
|
|
}
|
|
|
|
/* Jede Farbe trägt Text -- sie muss gegen den dunklen Grund halten. */
|
|
{
|
|
const schlecht = liste.filter((x) => kontrast(alsRgb(x.hex), GRUND) < 4.5);
|
|
ok(schlecht.length === 0,
|
|
schlecht.length
|
|
? `unter 4,5:1 -> ${schlecht.map((x) => `Ton ${x.n} ${x.hex} `
|
|
+ `${kontrast(alsRgb(x.hex), GRUND).toFixed(2)}:1`).join(", ")}`
|
|
: `alle halten 4,5:1 gegen den Grund (schlechteste `
|
|
+ `${Math.min(...liste.map((x) => kontrast(alsRgb(x.hex), GRUND))).toFixed(2)}:1)`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
2. DAS UNIVERSUM IST DA -- UND ÜBERALL
|
|
|
|
Gezählt wird im CSS: Wie viele Ebenen hat der Kachelhintergrund? Ein
|
|
Universum aus „ein paar weißen Punkten" hat drei, das gebaute hat
|
|
siebzehn. Die Zahl allein sagt nichts über Schönheit -- sie sagt,
|
|
dass die Ebenen NICHT VERSEHENTLICH verschwinden, und genau das ist
|
|
schon einmal passiert (die Leuchtschiene, 07.09.: Regeln blieben,
|
|
Inhalt weg).
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 2. Das Universum im Hintergrund ===");
|
|
{
|
|
const block = cssText.slice(cssText.indexOf(".kachel {"),
|
|
cssText.indexOf("background-size:", cssText.indexOf(".kachel {")));
|
|
/* DIE GROESSEN, NICHT NUR DIE ANZAHL.
|
|
|
|
Der erste Anlauf zaehlte `radial-gradient(...px` -- und `0px` ist
|
|
auch eine Zahl. Setzt man alle Sterne auf null, blieb die Zaehlung
|
|
bei zwoelf und die Pruefung gruen, waehrend das Universum leer war.
|
|
|
|
UND WARUM NICHT AM BILD GEMESSEN WIRD: Der zweite Anlauf zaehlte
|
|
Lichtpunkte im Bildschirmfoto. Auch das trug nicht -- die Kachel
|
|
ist halbdurchsichtig, darunter liegt das Buehnenbild, und ein Foto
|
|
hat selbst Punktstruktur. Gemessen: 459 Punkte mit Sternen, 441
|
|
ohne. Vier Prozent Unterschied sind kein Nachweis, sondern
|
|
Rauschen. Eine Pruefung, die das als "bestanden" wertet, prueft
|
|
nichts -- lieber eine ehrliche schwache als eine starke, die luegt. */
|
|
const groessen = [...block.matchAll(/radial-gradient\(\s*([\d.]+)px/g)]
|
|
.map((m) => Number(m[1]));
|
|
const sterne = groessen.filter((g) => g >= 0.7).length;
|
|
const nebel = (block.match(/radial-gradient\(\s*\d+%/g) || []).length;
|
|
ok(sterne >= 10,
|
|
`${sterne} Sternebenen mit sichtbarer Groesse `
|
|
+ `(${groessen.length} Punktebenen insgesamt, kleinste `
|
|
+ `${Math.min(...groessen).toFixed(1)}px)`);
|
|
ok(nebel >= 3, `${nebel} Nebel- und Tiefenebenen`);
|
|
{
|
|
/* GEZAEHLT, NICHT NUR GESUCHT. Der erste Anlauf fragte, ob
|
|
`var(--ton)` im Block VORKOMMT -- das tut es auch im
|
|
Grundverlauf, der seit jeher da ist. Die Gegenprobe (einem Nebel
|
|
die Kachelfarbe wegnehmen) blieb deshalb gruen. Gezaehlt werden
|
|
jetzt die FLAECHIGEN Verlaeufe, die den Ton wirklich tragen. */
|
|
const nebelMitTon = (block.match(
|
|
/radial-gradient\(\s*\d+%[^)]*\)[^,]*var\(--ton\)|radial-gradient\(\s*\d+%[^;]*?color-mix\(in srgb, var\(--ton\)/g) || []).length;
|
|
ok(nebelMitTon >= 2,
|
|
`${nebelMitTon} Nebel tragen die Farbe der Kachel `
|
|
+ "-- jede Kachel ihr eigenes Universum");
|
|
}
|
|
ok(/118deg|milchstra/i.test(block), "und es gibt eine Schräge (die Milchstraße)");
|
|
}
|
|
|
|
/* =======================================================================
|
|
3. DER TEXT DARAUF IST LESBAR
|
|
|
|
Der Abschnitt, den sonst niemand macht. Gemessen am Bildschirmfoto:
|
|
Zwischen Textfarbe und Fläche liegen sieben Ebenen plus das
|
|
durchscheinende Bühnenbild -- aus dem CSS ist das nicht mehr
|
|
auszurechnen.
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 3. Der Text auf der fertigen Kachel ===");
|
|
|
|
const PORT = await eigenerPort(import.meta, "pruef-kachel-universum");
|
|
const ordner = mkdtempSync(join(tmpdir(), "ws-kachel-"));
|
|
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
|
process.env.PORT = String(PORT);
|
|
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
|
process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00";
|
|
|
|
const express = (await import("express")).default;
|
|
const ec = express.response.cookie;
|
|
express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); };
|
|
const { notbremse } = await import("./helfer-notbremse.mjs");
|
|
await import("./index.js");
|
|
notbremse(180_000, "pruef-kachel-universum");
|
|
await new Promise((r) => setTimeout(r, 800));
|
|
|
|
{
|
|
const { DatabaseSync } = await import("node:sqlite");
|
|
const { scryptSync, randomBytes } = await import("node:crypto");
|
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
const salt = randomBytes(16).toString("hex");
|
|
const hash = scryptSync("CODE-DOGI-0001", salt, 64,
|
|
{ N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
|
|
d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n, aktiv, erstellt) VALUES (?,?,?,?,?,1,?)")
|
|
.run("Filipe", "admin", hash, salt, 32768, new Date().toISOString());
|
|
d.close();
|
|
}
|
|
|
|
let chromium;
|
|
try {
|
|
({ chromium } = await import(
|
|
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs"));
|
|
} catch { chromium = null; }
|
|
|
|
if (!chromium) {
|
|
unklar("kein Playwright gefunden -- die Kacheln wurden nicht angesehen");
|
|
} else {
|
|
const browser = await chromium.launch();
|
|
try {
|
|
const kontext = await browser.newContext({
|
|
viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2,
|
|
});
|
|
const seite = await kontext.newPage();
|
|
await seite.goto(`http://127.0.0.1:${PORT}/workspace/`, { waitUntil: "domcontentloaded" });
|
|
await seite.click('.rolle[data-rolle="admin"]');
|
|
await seite.fill("#code", "CODE-DOGI-0001");
|
|
await seite.click("button[type=submit]");
|
|
await seite.waitForURL("**/start.html", { timeout: 15000 });
|
|
await seite.waitForTimeout(2500);
|
|
|
|
/* ERST ZU DEN KACHELN SCROLLEN. Beim ersten Lauf wurden NULL
|
|
gemessen: Der Filter verlangt, dass die Kachel ganz im Bild
|
|
liegt, und oben steht die Zentrale mit Uhr und Zahlen. Eine
|
|
Messung, die nichts findet, ist keine Messung -- sie sah nur
|
|
aus wie eine. */
|
|
/* In die Mitte des Kachelfeldes, nicht an seinen Anfang: Dort
|
|
stehen mehrere Reihen gleichzeitig im Bild, und je mehr Kacheln
|
|
gemessen werden, desto eher ist die schlechteste dabei. */
|
|
const anzahl = await seite.locator(".kachel").count();
|
|
await seite.locator(".kachel").nth(Math.min(6, anzahl - 1)).scrollIntoViewIfNeeded();
|
|
await seite.waitForTimeout(700);
|
|
|
|
const stellen = await seite.evaluate(() =>
|
|
[...document.querySelectorAll(".kachel")].map((k) => {
|
|
const name = k.querySelector(".kachel__name");
|
|
if (!name) return null;
|
|
const r = name.getBoundingClientRect();
|
|
/* SICHTBAR HEISST: die Zeile liegt im Bild -- nicht die ganze
|
|
Kachel. Der erste Filter verlangte letzteres und liess genau
|
|
EINE uebrig; gemessen wird aber der Punkt unter dem TEXT,
|
|
und der braucht nur sich selbst. */
|
|
if (r.width < 10 || r.top < 8 || r.bottom > window.innerHeight - 8) return null;
|
|
const [tr, tg, tb] = getComputedStyle(name).color.match(/\d+/g).map(Number);
|
|
/* DIE KACHELGRENZEN KOMMEN MIT. Ohne sie wurde der Messbereich
|
|
aus der Textposition geschaetzt (`y - 70`) -- und lag damit
|
|
OBERHALB der Kachel, im Seitenhintergrund. Die Gegenprobe
|
|
lieferte zweimal exakt 153 Punkte; genau diese Gleichheit hat
|
|
den Fehler verraten. Eine Messung, deren Ergebnis sich nicht
|
|
aendert, wenn man das Gemessene entfernt, misst etwas
|
|
anderes. */
|
|
const kb = k.getBoundingClientRect();
|
|
return {
|
|
name: name.textContent.trim(), text: [tr, tg, tb],
|
|
x: Math.round(r.left + r.width / 2), y: Math.round(r.top + r.height / 2),
|
|
kachel: {
|
|
links: Math.round(kb.left), oben: Math.round(kb.top),
|
|
breite: Math.round(kb.width), hoehe: Math.round(kb.height),
|
|
},
|
|
};
|
|
}).filter(Boolean));
|
|
|
|
/* DIE ANZAHL GEHÖRT IN DIE BEDINGUNG: Ohne sichtbare Kacheln wäre
|
|
„alle lesbar" auch dann grün, wenn keine einzige gemessen wurde. */
|
|
ok(stellen.length >= 4, `${stellen.length} Kacheln im Bild gemessen`);
|
|
|
|
/* DER DRITTE AUSGANG. Ohne diesen Sprung stuerzte der Abschnitt ab
|
|
(`werte[0].k` auf einem leeren Feld) -- und ein Absturz sagt
|
|
weniger als ein Fehlschlag: Er verschluckt die Zeilen danach und
|
|
sieht aus wie ein Problem der Pruefung statt der Sache. */
|
|
if (!stellen.length) {
|
|
ok(false, "ohne sichtbare Kachel laesst sich kein Kontrast messen");
|
|
} else {
|
|
|
|
const bild = pngLesen(await seite.screenshot({ type: "png" }));
|
|
const werte = stellen.map((s) => {
|
|
/* Mehrere Punkte je Kachel -- der Untergrund ist nicht
|
|
gleichmäßig (Nebel, Vignette, Sterne). Der SCHLECHTESTE zählt:
|
|
An der hellsten Stelle steht der Text am schwersten. */
|
|
let schlecht = Infinity;
|
|
for (const dx of [-30, -15, 0, 15, 30]) {
|
|
for (const dy of [-4, 0, 4]) {
|
|
const k = kontrast(s.text, pixel(bild, (s.x + dx) * 2, (s.y + dy) * 2));
|
|
if (k < schlecht) schlecht = k;
|
|
}
|
|
}
|
|
return { name: s.name, k: schlecht };
|
|
}).sort((a, b) => a.k - b.k);
|
|
|
|
ok(stellen.length > 0 && werte[0].k >= 4.5,
|
|
`schlechtester Textkontrast ${werte[0].k.toFixed(2)}:1 auf „${werte[0].name}" (Grenze 4,5)`);
|
|
melde(` die drei schwächsten: ${werte.slice(0, 3)
|
|
.map((w) => `${w.name} ${w.k.toFixed(1)}:1`).join(" · ")}`);
|
|
|
|
|
|
}
|
|
|
|
await seite.screenshot({ path: "server/pruef-kachel-universum.png", fullPage: false });
|
|
melde(" -- Bildschirmfoto: server/pruef-kachel-universum.png");
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
}
|
|
|
|
melde("");
|
|
melde(`${geprueft} Pruefungen, ${fehler} Fehler`
|
|
+ (offen ? `, ${offen} nicht nachsehbar` : ""));
|
|
melde(fehler ? "NICHT IN ORDNUNG" : "ALLES IN ORDNUNG");
|
|
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Aufraeumrest */ }
|
|
process.exit(fehler ? 1 : 0);
|